@proofhound/core 0.1.6

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 (670) hide show
  1. package/LICENSE +190 -0
  2. package/dist/index.d.ts +7 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +10 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/infra/index.d.ts +12 -0
  7. package/dist/infra/index.d.ts.map +1 -0
  8. package/dist/infra/index.js +27 -0
  9. package/dist/infra/index.js.map +1 -0
  10. package/dist/server/channels/mcp/annotation.tools.d.ts +4 -0
  11. package/dist/server/channels/mcp/annotation.tools.d.ts.map +1 -0
  12. package/dist/server/channels/mcp/annotation.tools.js +132 -0
  13. package/dist/server/channels/mcp/annotation.tools.js.map +1 -0
  14. package/dist/server/channels/mcp/canary-release.tools.d.ts +4 -0
  15. package/dist/server/channels/mcp/canary-release.tools.d.ts.map +1 -0
  16. package/dist/server/channels/mcp/canary-release.tools.js +279 -0
  17. package/dist/server/channels/mcp/canary-release.tools.js.map +1 -0
  18. package/dist/server/channels/mcp/connector.tools.d.ts +4 -0
  19. package/dist/server/channels/mcp/connector.tools.d.ts.map +1 -0
  20. package/dist/server/channels/mcp/connector.tools.js +211 -0
  21. package/dist/server/channels/mcp/connector.tools.js.map +1 -0
  22. package/dist/server/channels/mcp/dataset-import.tools.d.ts +4 -0
  23. package/dist/server/channels/mcp/dataset-import.tools.d.ts.map +1 -0
  24. package/dist/server/channels/mcp/dataset-import.tools.js +107 -0
  25. package/dist/server/channels/mcp/dataset-import.tools.js.map +1 -0
  26. package/dist/server/channels/mcp/dataset.tools.d.ts +4 -0
  27. package/dist/server/channels/mcp/dataset.tools.d.ts.map +1 -0
  28. package/dist/server/channels/mcp/dataset.tools.js +166 -0
  29. package/dist/server/channels/mcp/dataset.tools.js.map +1 -0
  30. package/dist/server/channels/mcp/experiment.tools.d.ts +4 -0
  31. package/dist/server/channels/mcp/experiment.tools.d.ts.map +1 -0
  32. package/dist/server/channels/mcp/experiment.tools.js +133 -0
  33. package/dist/server/channels/mcp/experiment.tools.js.map +1 -0
  34. package/dist/server/channels/mcp/index.d.ts +19 -0
  35. package/dist/server/channels/mcp/index.d.ts.map +1 -0
  36. package/dist/server/channels/mcp/index.js +35 -0
  37. package/dist/server/channels/mcp/index.js.map +1 -0
  38. package/dist/server/channels/mcp/mcp-context.d.ts +30 -0
  39. package/dist/server/channels/mcp/mcp-context.d.ts.map +1 -0
  40. package/dist/server/channels/mcp/mcp-context.js +110 -0
  41. package/dist/server/channels/mcp/mcp-context.js.map +1 -0
  42. package/dist/server/channels/mcp/mcp-server.factory.d.ts +13 -0
  43. package/dist/server/channels/mcp/mcp-server.factory.d.ts.map +1 -0
  44. package/dist/server/channels/mcp/mcp-server.factory.js +42 -0
  45. package/dist/server/channels/mcp/mcp-server.factory.js.map +1 -0
  46. package/dist/server/channels/mcp/mcp.controller.d.ts +10 -0
  47. package/dist/server/channels/mcp/mcp.controller.d.ts.map +1 -0
  48. package/dist/server/channels/mcp/mcp.controller.js +67 -0
  49. package/dist/server/channels/mcp/mcp.controller.js.map +1 -0
  50. package/dist/server/channels/mcp/mcp.module.d.ts +3 -0
  51. package/dist/server/channels/mcp/mcp.module.d.ts.map +1 -0
  52. package/dist/server/channels/mcp/mcp.module.js +114 -0
  53. package/dist/server/channels/mcp/mcp.module.js.map +1 -0
  54. package/dist/server/channels/mcp/mcp.tokens.d.ts +2 -0
  55. package/dist/server/channels/mcp/mcp.tokens.d.ts.map +1 -0
  56. package/dist/server/channels/mcp/mcp.tokens.js +8 -0
  57. package/dist/server/channels/mcp/mcp.tokens.js.map +1 -0
  58. package/dist/server/channels/mcp/mcp.transport.d.ts +11 -0
  59. package/dist/server/channels/mcp/mcp.transport.d.ts.map +1 -0
  60. package/dist/server/channels/mcp/mcp.transport.js +71 -0
  61. package/dist/server/channels/mcp/mcp.transport.js.map +1 -0
  62. package/dist/server/channels/mcp/mcp.types.d.ts +16 -0
  63. package/dist/server/channels/mcp/mcp.types.d.ts.map +1 -0
  64. package/dist/server/channels/mcp/mcp.types.js +3 -0
  65. package/dist/server/channels/mcp/mcp.types.js.map +1 -0
  66. package/dist/server/channels/mcp/model.tools.d.ts +4 -0
  67. package/dist/server/channels/mcp/model.tools.d.ts.map +1 -0
  68. package/dist/server/channels/mcp/model.tools.js +215 -0
  69. package/dist/server/channels/mcp/model.tools.js.map +1 -0
  70. package/dist/server/channels/mcp/monitoring.tools.d.ts +4 -0
  71. package/dist/server/channels/mcp/monitoring.tools.d.ts.map +1 -0
  72. package/dist/server/channels/mcp/monitoring.tools.js +81 -0
  73. package/dist/server/channels/mcp/monitoring.tools.js.map +1 -0
  74. package/dist/server/channels/mcp/optimization.tools.d.ts +4 -0
  75. package/dist/server/channels/mcp/optimization.tools.d.ts.map +1 -0
  76. package/dist/server/channels/mcp/optimization.tools.js +104 -0
  77. package/dist/server/channels/mcp/optimization.tools.js.map +1 -0
  78. package/dist/server/channels/mcp/prompt.tools.d.ts +5 -0
  79. package/dist/server/channels/mcp/prompt.tools.d.ts.map +1 -0
  80. package/dist/server/channels/mcp/prompt.tools.js +258 -0
  81. package/dist/server/channels/mcp/prompt.tools.js.map +1 -0
  82. package/dist/server/channels/mcp/quick-start.tools.d.ts +4 -0
  83. package/dist/server/channels/mcp/quick-start.tools.d.ts.map +1 -0
  84. package/dist/server/channels/mcp/quick-start.tools.js +48 -0
  85. package/dist/server/channels/mcp/quick-start.tools.js.map +1 -0
  86. package/dist/server/channels/mcp/release-line.tools.d.ts +4 -0
  87. package/dist/server/channels/mcp/release-line.tools.d.ts.map +1 -0
  88. package/dist/server/channels/mcp/release-line.tools.js +101 -0
  89. package/dist/server/channels/mcp/release-line.tools.js.map +1 -0
  90. package/dist/server/channels/mcp/run-result.tools.d.ts +4 -0
  91. package/dist/server/channels/mcp/run-result.tools.d.ts.map +1 -0
  92. package/dist/server/channels/mcp/run-result.tools.js +116 -0
  93. package/dist/server/channels/mcp/run-result.tools.js.map +1 -0
  94. package/dist/server/channels/mcp/token.tools.d.ts +4 -0
  95. package/dist/server/channels/mcp/token.tools.d.ts.map +1 -0
  96. package/dist/server/channels/mcp/token.tools.js +87 -0
  97. package/dist/server/channels/mcp/token.tools.js.map +1 -0
  98. package/dist/server/common/access-control.d.ts +5 -0
  99. package/dist/server/common/access-control.d.ts.map +1 -0
  100. package/dist/server/common/access-control.js +25 -0
  101. package/dist/server/common/access-control.js.map +1 -0
  102. package/dist/server/common/actor-context.d.ts +11 -0
  103. package/dist/server/common/actor-context.d.ts.map +1 -0
  104. package/dist/server/common/actor-context.js +8 -0
  105. package/dist/server/common/actor-context.js.map +1 -0
  106. package/dist/server/common/contracts/access-control.service.d.ts +6 -0
  107. package/dist/server/common/contracts/access-control.service.d.ts.map +1 -0
  108. package/dist/server/common/contracts/access-control.service.js +13 -0
  109. package/dist/server/common/contracts/access-control.service.js.map +1 -0
  110. package/dist/server/common/contracts/actor-context.resolver.d.ts +7 -0
  111. package/dist/server/common/contracts/actor-context.resolver.d.ts.map +1 -0
  112. package/dist/server/common/contracts/actor-context.resolver.js +15 -0
  113. package/dist/server/common/contracts/actor-context.resolver.js.map +1 -0
  114. package/dist/server/common/contracts/connector-context.resolver.d.ts +19 -0
  115. package/dist/server/common/contracts/connector-context.resolver.d.ts.map +1 -0
  116. package/dist/server/common/contracts/connector-context.resolver.js +23 -0
  117. package/dist/server/common/contracts/connector-context.resolver.js.map +1 -0
  118. package/dist/server/common/contracts/http-actor.guard.d.ts +13 -0
  119. package/dist/server/common/contracts/http-actor.guard.d.ts.map +1 -0
  120. package/dist/server/common/contracts/http-actor.guard.js +87 -0
  121. package/dist/server/common/contracts/http-actor.guard.js.map +1 -0
  122. package/dist/server/common/contracts/index.d.ts +23 -0
  123. package/dist/server/common/contracts/index.d.ts.map +1 -0
  124. package/dist/server/common/contracts/index.js +45 -0
  125. package/dist/server/common/contracts/index.js.map +1 -0
  126. package/dist/server/common/contracts/limiter-key.strategy.d.ts +8 -0
  127. package/dist/server/common/contracts/limiter-key.strategy.d.ts.map +1 -0
  128. package/dist/server/common/contracts/limiter-key.strategy.js +24 -0
  129. package/dist/server/common/contracts/limiter-key.strategy.js.map +1 -0
  130. package/dist/server/common/contracts/local-access-control.service.d.ts +7 -0
  131. package/dist/server/common/contracts/local-access-control.service.d.ts.map +1 -0
  132. package/dist/server/common/contracts/local-access-control.service.js +41 -0
  133. package/dist/server/common/contracts/local-access-control.service.js.map +1 -0
  134. package/dist/server/common/contracts/local-actor-context.resolver.d.ts +13 -0
  135. package/dist/server/common/contracts/local-actor-context.resolver.d.ts.map +1 -0
  136. package/dist/server/common/contracts/local-actor-context.resolver.js +102 -0
  137. package/dist/server/common/contracts/local-actor-context.resolver.js.map +1 -0
  138. package/dist/server/common/contracts/local-contracts.module.d.ts +3 -0
  139. package/dist/server/common/contracts/local-contracts.module.d.ts.map +1 -0
  140. package/dist/server/common/contracts/local-contracts.module.js +78 -0
  141. package/dist/server/common/contracts/local-contracts.module.js.map +1 -0
  142. package/dist/server/common/contracts/local-mcp-auth.resolver.d.ts +12 -0
  143. package/dist/server/common/contracts/local-mcp-auth.resolver.d.ts.map +1 -0
  144. package/dist/server/common/contracts/local-mcp-auth.resolver.js +66 -0
  145. package/dist/server/common/contracts/local-mcp-auth.resolver.js.map +1 -0
  146. package/dist/server/common/contracts/local-project-context.resolver.d.ts +8 -0
  147. package/dist/server/common/contracts/local-project-context.resolver.d.ts.map +1 -0
  148. package/dist/server/common/contracts/local-project-context.resolver.js +27 -0
  149. package/dist/server/common/contracts/local-project-context.resolver.js.map +1 -0
  150. package/dist/server/common/contracts/local-user-token.verifier.d.ts +26 -0
  151. package/dist/server/common/contracts/local-user-token.verifier.d.ts.map +1 -0
  152. package/dist/server/common/contracts/local-user-token.verifier.js +96 -0
  153. package/dist/server/common/contracts/local-user-token.verifier.js.map +1 -0
  154. package/dist/server/common/contracts/mcp-auth.resolver.d.ts +7 -0
  155. package/dist/server/common/contracts/mcp-auth.resolver.d.ts.map +1 -0
  156. package/dist/server/common/contracts/mcp-auth.resolver.js +12 -0
  157. package/dist/server/common/contracts/mcp-auth.resolver.js.map +1 -0
  158. package/dist/server/common/contracts/project-context.resolver.d.ts +11 -0
  159. package/dist/server/common/contracts/project-context.resolver.d.ts.map +1 -0
  160. package/dist/server/common/contracts/project-context.resolver.js +22 -0
  161. package/dist/server/common/contracts/project-context.resolver.js.map +1 -0
  162. package/dist/server/common/contracts/quota-policy.hook.d.ts +24 -0
  163. package/dist/server/common/contracts/quota-policy.hook.d.ts.map +1 -0
  164. package/dist/server/common/contracts/quota-policy.hook.js +22 -0
  165. package/dist/server/common/contracts/quota-policy.hook.js.map +1 -0
  166. package/dist/server/common/contracts/runtime-limits.provider.d.ts +17 -0
  167. package/dist/server/common/contracts/runtime-limits.provider.d.ts.map +1 -0
  168. package/dist/server/common/contracts/runtime-limits.provider.js +23 -0
  169. package/dist/server/common/contracts/runtime-limits.provider.js.map +1 -0
  170. package/dist/server/common/contracts/token.service.d.ts +11 -0
  171. package/dist/server/common/contracts/token.service.d.ts.map +1 -0
  172. package/dist/server/common/contracts/token.service.js +12 -0
  173. package/dist/server/common/contracts/token.service.js.map +1 -0
  174. package/dist/server/common/contracts/types.d.ts +45 -0
  175. package/dist/server/common/contracts/types.d.ts.map +1 -0
  176. package/dist/server/common/contracts/types.js +8 -0
  177. package/dist/server/common/contracts/types.js.map +1 -0
  178. package/dist/server/common/contracts/workflow-authorization.hook.d.ts +9 -0
  179. package/dist/server/common/contracts/workflow-authorization.hook.d.ts.map +1 -0
  180. package/dist/server/common/contracts/workflow-authorization.hook.js +27 -0
  181. package/dist/server/common/contracts/workflow-authorization.hook.js.map +1 -0
  182. package/dist/server/common/decorators/current-project.decorator.d.ts +5 -0
  183. package/dist/server/common/decorators/current-project.decorator.d.ts.map +1 -0
  184. package/dist/server/common/decorators/current-project.decorator.js +22 -0
  185. package/dist/server/common/decorators/current-project.decorator.js.map +1 -0
  186. package/dist/server/common/decorators/current-user.decorator.d.ts +13 -0
  187. package/dist/server/common/decorators/current-user.decorator.d.ts.map +1 -0
  188. package/dist/server/common/decorators/current-user.decorator.js +9 -0
  189. package/dist/server/common/decorators/current-user.decorator.js.map +1 -0
  190. package/dist/server/common/errors/db-error.d.ts +2 -0
  191. package/dist/server/common/errors/db-error.d.ts.map +1 -0
  192. package/dist/server/common/errors/db-error.js +12 -0
  193. package/dist/server/common/errors/db-error.js.map +1 -0
  194. package/dist/server/common/project-context.d.ts +22 -0
  195. package/dist/server/common/project-context.d.ts.map +1 -0
  196. package/dist/server/common/project-context.js +60 -0
  197. package/dist/server/common/project-context.js.map +1 -0
  198. package/dist/server/common/project-context.module.d.ts +3 -0
  199. package/dist/server/common/project-context.module.d.ts.map +1 -0
  200. package/dist/server/common/project-context.module.js +22 -0
  201. package/dist/server/common/project-context.module.js.map +1 -0
  202. package/dist/server/index.d.ts +4 -0
  203. package/dist/server/index.d.ts.map +1 -0
  204. package/dist/server/index.js +8 -0
  205. package/dist/server/index.js.map +1 -0
  206. package/dist/server/infrastructure/llm/run-result-writer.d.ts +10 -0
  207. package/dist/server/infrastructure/llm/run-result-writer.d.ts.map +1 -0
  208. package/dist/server/infrastructure/llm/run-result-writer.js +103 -0
  209. package/dist/server/infrastructure/llm/run-result-writer.js.map +1 -0
  210. package/dist/server/infrastructure/orchestration/bullmq.module.d.ts +3 -0
  211. package/dist/server/infrastructure/orchestration/bullmq.module.d.ts.map +1 -0
  212. package/dist/server/infrastructure/orchestration/bullmq.module.js +36 -0
  213. package/dist/server/infrastructure/orchestration/bullmq.module.js.map +1 -0
  214. package/dist/server/infrastructure/orchestration/bullmq.service.d.ts +10 -0
  215. package/dist/server/infrastructure/orchestration/bullmq.service.d.ts.map +1 -0
  216. package/dist/server/infrastructure/orchestration/bullmq.service.js +44 -0
  217. package/dist/server/infrastructure/orchestration/bullmq.service.js.map +1 -0
  218. package/dist/server/infrastructure/orchestration/dbos.module.d.ts +3 -0
  219. package/dist/server/infrastructure/orchestration/dbos.module.d.ts.map +1 -0
  220. package/dist/server/infrastructure/orchestration/dbos.module.js +21 -0
  221. package/dist/server/infrastructure/orchestration/dbos.module.js.map +1 -0
  222. package/dist/server/infrastructure/orchestration/dbos.service.d.ts +7 -0
  223. package/dist/server/infrastructure/orchestration/dbos.service.d.ts.map +1 -0
  224. package/dist/server/infrastructure/orchestration/dbos.service.js +43 -0
  225. package/dist/server/infrastructure/orchestration/dbos.service.js.map +1 -0
  226. package/dist/server/infrastructure/orchestration/index.d.ts +6 -0
  227. package/dist/server/infrastructure/orchestration/index.d.ts.map +1 -0
  228. package/dist/server/infrastructure/orchestration/index.js +14 -0
  229. package/dist/server/infrastructure/orchestration/index.js.map +1 -0
  230. package/dist/server/infrastructure/orchestration/orchestration.constants.d.ts +2 -0
  231. package/dist/server/infrastructure/orchestration/orchestration.constants.d.ts.map +1 -0
  232. package/dist/server/infrastructure/orchestration/orchestration.constants.js +5 -0
  233. package/dist/server/infrastructure/orchestration/orchestration.constants.js.map +1 -0
  234. package/dist/server/infrastructure/orchestration/orchestration.module.d.ts +3 -0
  235. package/dist/server/infrastructure/orchestration/orchestration.module.d.ts.map +1 -0
  236. package/dist/server/infrastructure/orchestration/orchestration.module.js +22 -0
  237. package/dist/server/infrastructure/orchestration/orchestration.module.js.map +1 -0
  238. package/dist/server/modules/annotation/annotation.controller.d.ts +245 -0
  239. package/dist/server/modules/annotation/annotation.controller.d.ts.map +1 -0
  240. package/dist/server/modules/annotation/annotation.controller.js +159 -0
  241. package/dist/server/modules/annotation/annotation.controller.js.map +1 -0
  242. package/dist/server/modules/annotation/annotation.module.d.ts +3 -0
  243. package/dist/server/modules/annotation/annotation.module.d.ts.map +1 -0
  244. package/dist/server/modules/annotation/annotation.module.js +26 -0
  245. package/dist/server/modules/annotation/annotation.module.js.map +1 -0
  246. package/dist/server/modules/annotation/annotation.repository.d.ts +28 -0
  247. package/dist/server/modules/annotation/annotation.repository.d.ts.map +1 -0
  248. package/dist/server/modules/annotation/annotation.repository.js +634 -0
  249. package/dist/server/modules/annotation/annotation.repository.js.map +1 -0
  250. package/dist/server/modules/annotation/annotation.service.d.ts +26 -0
  251. package/dist/server/modules/annotation/annotation.service.d.ts.map +1 -0
  252. package/dist/server/modules/annotation/annotation.service.js +144 -0
  253. package/dist/server/modules/annotation/annotation.service.js.map +1 -0
  254. package/dist/server/modules/canary-release/canary-release.controller.d.ts +629 -0
  255. package/dist/server/modules/canary-release/canary-release.controller.d.ts.map +1 -0
  256. package/dist/server/modules/canary-release/canary-release.controller.js +244 -0
  257. package/dist/server/modules/canary-release/canary-release.controller.js.map +1 -0
  258. package/dist/server/modules/canary-release/canary-release.module.d.ts +3 -0
  259. package/dist/server/modules/canary-release/canary-release.module.d.ts.map +1 -0
  260. package/dist/server/modules/canary-release/canary-release.module.js +27 -0
  261. package/dist/server/modules/canary-release/canary-release.module.js.map +1 -0
  262. package/dist/server/modules/canary-release/canary-release.repository.d.ts +686 -0
  263. package/dist/server/modules/canary-release/canary-release.repository.d.ts.map +1 -0
  264. package/dist/server/modules/canary-release/canary-release.repository.js +673 -0
  265. package/dist/server/modules/canary-release/canary-release.repository.js.map +1 -0
  266. package/dist/server/modules/canary-release/canary-release.service.d.ts +55 -0
  267. package/dist/server/modules/canary-release/canary-release.service.d.ts.map +1 -0
  268. package/dist/server/modules/canary-release/canary-release.service.js +573 -0
  269. package/dist/server/modules/canary-release/canary-release.service.js.map +1 -0
  270. package/dist/server/modules/canary-release/canary-runtime.d.ts +60 -0
  271. package/dist/server/modules/canary-release/canary-runtime.d.ts.map +1 -0
  272. package/dist/server/modules/canary-release/canary-runtime.js +328 -0
  273. package/dist/server/modules/canary-release/canary-runtime.js.map +1 -0
  274. package/dist/server/modules/connector/connector.controller.d.ts +446 -0
  275. package/dist/server/modules/connector/connector.controller.d.ts.map +1 -0
  276. package/dist/server/modules/connector/connector.controller.js +235 -0
  277. package/dist/server/modules/connector/connector.controller.js.map +1 -0
  278. package/dist/server/modules/connector/connector.driver-factory.d.ts +66 -0
  279. package/dist/server/modules/connector/connector.driver-factory.d.ts.map +1 -0
  280. package/dist/server/modules/connector/connector.driver-factory.js +314 -0
  281. package/dist/server/modules/connector/connector.driver-factory.js.map +1 -0
  282. package/dist/server/modules/connector/connector.module.d.ts +3 -0
  283. package/dist/server/modules/connector/connector.module.d.ts.map +1 -0
  284. package/dist/server/modules/connector/connector.module.js +28 -0
  285. package/dist/server/modules/connector/connector.module.js.map +1 -0
  286. package/dist/server/modules/connector/connector.repository.d.ts +369 -0
  287. package/dist/server/modules/connector/connector.repository.d.ts.map +1 -0
  288. package/dist/server/modules/connector/connector.repository.js +323 -0
  289. package/dist/server/modules/connector/connector.repository.js.map +1 -0
  290. package/dist/server/modules/connector/connector.service.d.ts +60 -0
  291. package/dist/server/modules/connector/connector.service.d.ts.map +1 -0
  292. package/dist/server/modules/connector/connector.service.js +682 -0
  293. package/dist/server/modules/connector/connector.service.js.map +1 -0
  294. package/dist/server/modules/dataset/dataset-field-schema.util.d.ts +4 -0
  295. package/dist/server/modules/dataset/dataset-field-schema.util.d.ts.map +1 -0
  296. package/dist/server/modules/dataset/dataset-field-schema.util.js +57 -0
  297. package/dist/server/modules/dataset/dataset-field-schema.util.js.map +1 -0
  298. package/dist/server/modules/dataset/dataset-import.controller.d.ts +79 -0
  299. package/dist/server/modules/dataset/dataset-import.controller.d.ts.map +1 -0
  300. package/dist/server/modules/dataset/dataset-import.controller.js +124 -0
  301. package/dist/server/modules/dataset/dataset-import.controller.js.map +1 -0
  302. package/dist/server/modules/dataset/dataset-import.repository.d.ts +63 -0
  303. package/dist/server/modules/dataset/dataset-import.repository.d.ts.map +1 -0
  304. package/dist/server/modules/dataset/dataset-import.repository.js +176 -0
  305. package/dist/server/modules/dataset/dataset-import.repository.js.map +1 -0
  306. package/dist/server/modules/dataset/dataset-import.service.d.ts +36 -0
  307. package/dist/server/modules/dataset/dataset-import.service.d.ts.map +1 -0
  308. package/dist/server/modules/dataset/dataset-import.service.js +227 -0
  309. package/dist/server/modules/dataset/dataset-import.service.js.map +1 -0
  310. package/dist/server/modules/dataset/dataset.controller.d.ts +127 -0
  311. package/dist/server/modules/dataset/dataset.controller.d.ts.map +1 -0
  312. package/dist/server/modules/dataset/dataset.controller.js +168 -0
  313. package/dist/server/modules/dataset/dataset.controller.js.map +1 -0
  314. package/dist/server/modules/dataset/dataset.module.d.ts +3 -0
  315. package/dist/server/modules/dataset/dataset.module.d.ts.map +1 -0
  316. package/dist/server/modules/dataset/dataset.module.js +29 -0
  317. package/dist/server/modules/dataset/dataset.module.js.map +1 -0
  318. package/dist/server/modules/dataset/dataset.repository.d.ts +75 -0
  319. package/dist/server/modules/dataset/dataset.repository.d.ts.map +1 -0
  320. package/dist/server/modules/dataset/dataset.repository.js +238 -0
  321. package/dist/server/modules/dataset/dataset.repository.js.map +1 -0
  322. package/dist/server/modules/dataset/dataset.service.d.ts +50 -0
  323. package/dist/server/modules/dataset/dataset.service.d.ts.map +1 -0
  324. package/dist/server/modules/dataset/dataset.service.js +343 -0
  325. package/dist/server/modules/dataset/dataset.service.js.map +1 -0
  326. package/dist/server/modules/experiment/experiment.aggregator.d.ts +14 -0
  327. package/dist/server/modules/experiment/experiment.aggregator.d.ts.map +1 -0
  328. package/dist/server/modules/experiment/experiment.aggregator.js +39 -0
  329. package/dist/server/modules/experiment/experiment.aggregator.js.map +1 -0
  330. package/dist/server/modules/experiment/experiment.controller.d.ts +361 -0
  331. package/dist/server/modules/experiment/experiment.controller.d.ts.map +1 -0
  332. package/dist/server/modules/experiment/experiment.controller.js +162 -0
  333. package/dist/server/modules/experiment/experiment.controller.js.map +1 -0
  334. package/dist/server/modules/experiment/experiment.launcher.d.ts +14 -0
  335. package/dist/server/modules/experiment/experiment.launcher.d.ts.map +1 -0
  336. package/dist/server/modules/experiment/experiment.launcher.js +54 -0
  337. package/dist/server/modules/experiment/experiment.launcher.js.map +1 -0
  338. package/dist/server/modules/experiment/experiment.module.d.ts +3 -0
  339. package/dist/server/modules/experiment/experiment.module.d.ts.map +1 -0
  340. package/dist/server/modules/experiment/experiment.module.js +38 -0
  341. package/dist/server/modules/experiment/experiment.module.js.map +1 -0
  342. package/dist/server/modules/experiment/experiment.recovery.d.ts +15 -0
  343. package/dist/server/modules/experiment/experiment.recovery.d.ts.map +1 -0
  344. package/dist/server/modules/experiment/experiment.recovery.js +80 -0
  345. package/dist/server/modules/experiment/experiment.recovery.js.map +1 -0
  346. package/dist/server/modules/experiment/experiment.renderer.d.ts +2 -0
  347. package/dist/server/modules/experiment/experiment.renderer.d.ts.map +1 -0
  348. package/dist/server/modules/experiment/experiment.renderer.js +6 -0
  349. package/dist/server/modules/experiment/experiment.renderer.js.map +1 -0
  350. package/dist/server/modules/experiment/experiment.repository.d.ts +88 -0
  351. package/dist/server/modules/experiment/experiment.repository.d.ts.map +1 -0
  352. package/dist/server/modules/experiment/experiment.repository.js +173 -0
  353. package/dist/server/modules/experiment/experiment.repository.js.map +1 -0
  354. package/dist/server/modules/experiment/experiment.service.d.ts +57 -0
  355. package/dist/server/modules/experiment/experiment.service.d.ts.map +1 -0
  356. package/dist/server/modules/experiment/experiment.service.js +539 -0
  357. package/dist/server/modules/experiment/experiment.service.js.map +1 -0
  358. package/dist/server/modules/experiment/experiment.workflow.d.ts +51 -0
  359. package/dist/server/modules/experiment/experiment.workflow.d.ts.map +1 -0
  360. package/dist/server/modules/experiment/experiment.workflow.js +465 -0
  361. package/dist/server/modules/experiment/experiment.workflow.js.map +1 -0
  362. package/dist/server/modules/model/model.controller.d.ts +23 -0
  363. package/dist/server/modules/model/model.controller.d.ts.map +1 -0
  364. package/dist/server/modules/model/model.controller.js +76 -0
  365. package/dist/server/modules/model/model.controller.js.map +1 -0
  366. package/dist/server/modules/model/model.module.d.ts +3 -0
  367. package/dist/server/modules/model/model.module.d.ts.map +1 -0
  368. package/dist/server/modules/model/model.module.js +29 -0
  369. package/dist/server/modules/model/model.module.js.map +1 -0
  370. package/dist/server/modules/model/model.repository.d.ts +511 -0
  371. package/dist/server/modules/model/model.repository.d.ts.map +1 -0
  372. package/dist/server/modules/model/model.repository.js +181 -0
  373. package/dist/server/modules/model/model.repository.js.map +1 -0
  374. package/dist/server/modules/model/model.service.d.ts +79 -0
  375. package/dist/server/modules/model/model.service.d.ts.map +1 -0
  376. package/dist/server/modules/model/model.service.js +643 -0
  377. package/dist/server/modules/model/model.service.js.map +1 -0
  378. package/dist/server/modules/model/project-model.controller.d.ts +261 -0
  379. package/dist/server/modules/model/project-model.controller.d.ts.map +1 -0
  380. package/dist/server/modules/model/project-model.controller.js +198 -0
  381. package/dist/server/modules/model/project-model.controller.js.map +1 -0
  382. package/dist/server/modules/monitoring/monitoring.controller.d.ts +204 -0
  383. package/dist/server/modules/monitoring/monitoring.controller.d.ts.map +1 -0
  384. package/dist/server/modules/monitoring/monitoring.controller.js +129 -0
  385. package/dist/server/modules/monitoring/monitoring.controller.js.map +1 -0
  386. package/dist/server/modules/monitoring/monitoring.module.d.ts +3 -0
  387. package/dist/server/modules/monitoring/monitoring.module.d.ts.map +1 -0
  388. package/dist/server/modules/monitoring/monitoring.module.js +26 -0
  389. package/dist/server/modules/monitoring/monitoring.module.js.map +1 -0
  390. package/dist/server/modules/monitoring/monitoring.repository.d.ts +15 -0
  391. package/dist/server/modules/monitoring/monitoring.repository.d.ts.map +1 -0
  392. package/dist/server/modules/monitoring/monitoring.repository.js +618 -0
  393. package/dist/server/modules/monitoring/monitoring.repository.js.map +1 -0
  394. package/dist/server/modules/monitoring/monitoring.service.d.ts +15 -0
  395. package/dist/server/modules/monitoring/monitoring.service.d.ts.map +1 -0
  396. package/dist/server/modules/monitoring/monitoring.service.js +70 -0
  397. package/dist/server/modules/monitoring/monitoring.service.js.map +1 -0
  398. package/dist/server/modules/optimization/optimization.controller.d.ts +579 -0
  399. package/dist/server/modules/optimization/optimization.controller.d.ts.map +1 -0
  400. package/dist/server/modules/optimization/optimization.controller.js +119 -0
  401. package/dist/server/modules/optimization/optimization.controller.js.map +1 -0
  402. package/dist/server/modules/optimization/optimization.launcher.d.ts +14 -0
  403. package/dist/server/modules/optimization/optimization.launcher.d.ts.map +1 -0
  404. package/dist/server/modules/optimization/optimization.launcher.js +56 -0
  405. package/dist/server/modules/optimization/optimization.launcher.js.map +1 -0
  406. package/dist/server/modules/optimization/optimization.module.d.ts +3 -0
  407. package/dist/server/modules/optimization/optimization.module.d.ts.map +1 -0
  408. package/dist/server/modules/optimization/optimization.module.js +42 -0
  409. package/dist/server/modules/optimization/optimization.module.js.map +1 -0
  410. package/dist/server/modules/optimization/optimization.recovery.d.ts +15 -0
  411. package/dist/server/modules/optimization/optimization.recovery.d.ts.map +1 -0
  412. package/dist/server/modules/optimization/optimization.recovery.js +80 -0
  413. package/dist/server/modules/optimization/optimization.recovery.js.map +1 -0
  414. package/dist/server/modules/optimization/optimization.repository.d.ts +375 -0
  415. package/dist/server/modules/optimization/optimization.repository.d.ts.map +1 -0
  416. package/dist/server/modules/optimization/optimization.repository.js +849 -0
  417. package/dist/server/modules/optimization/optimization.repository.js.map +1 -0
  418. package/dist/server/modules/optimization/optimization.service.d.ts +74 -0
  419. package/dist/server/modules/optimization/optimization.service.d.ts.map +1 -0
  420. package/dist/server/modules/optimization/optimization.service.js +1753 -0
  421. package/dist/server/modules/optimization/optimization.service.js.map +1 -0
  422. package/dist/server/modules/optimization/optimization.workflow.d.ts +118 -0
  423. package/dist/server/modules/optimization/optimization.workflow.d.ts.map +1 -0
  424. package/dist/server/modules/optimization/optimization.workflow.js +2007 -0
  425. package/dist/server/modules/optimization/optimization.workflow.js.map +1 -0
  426. package/dist/server/modules/production-release/production-release.controller.d.ts +125 -0
  427. package/dist/server/modules/production-release/production-release.controller.d.ts.map +1 -0
  428. package/dist/server/modules/production-release/production-release.controller.js +107 -0
  429. package/dist/server/modules/production-release/production-release.controller.js.map +1 -0
  430. package/dist/server/modules/production-release/production-release.module.d.ts +3 -0
  431. package/dist/server/modules/production-release/production-release.module.d.ts.map +1 -0
  432. package/dist/server/modules/production-release/production-release.module.js +27 -0
  433. package/dist/server/modules/production-release/production-release.module.js.map +1 -0
  434. package/dist/server/modules/production-release/production-release.repository.d.ts +109 -0
  435. package/dist/server/modules/production-release/production-release.repository.d.ts.map +1 -0
  436. package/dist/server/modules/production-release/production-release.repository.js +372 -0
  437. package/dist/server/modules/production-release/production-release.repository.js.map +1 -0
  438. package/dist/server/modules/production-release/production-release.service.d.ts +33 -0
  439. package/dist/server/modules/production-release/production-release.service.d.ts.map +1 -0
  440. package/dist/server/modules/production-release/production-release.service.js +370 -0
  441. package/dist/server/modules/production-release/production-release.service.js.map +1 -0
  442. package/dist/server/modules/prompt/prompt-try-run.service.d.ts +26 -0
  443. package/dist/server/modules/prompt/prompt-try-run.service.d.ts.map +1 -0
  444. package/dist/server/modules/prompt/prompt-try-run.service.js +213 -0
  445. package/dist/server/modules/prompt/prompt-try-run.service.js.map +1 -0
  446. package/dist/server/modules/prompt/prompt.controller.d.ts +489 -0
  447. package/dist/server/modules/prompt/prompt.controller.d.ts.map +1 -0
  448. package/dist/server/modules/prompt/prompt.controller.js +236 -0
  449. package/dist/server/modules/prompt/prompt.controller.js.map +1 -0
  450. package/dist/server/modules/prompt/prompt.module.d.ts +3 -0
  451. package/dist/server/modules/prompt/prompt.module.d.ts.map +1 -0
  452. package/dist/server/modules/prompt/prompt.module.js +28 -0
  453. package/dist/server/modules/prompt/prompt.module.js.map +1 -0
  454. package/dist/server/modules/prompt/prompt.repository.d.ts +190 -0
  455. package/dist/server/modules/prompt/prompt.repository.d.ts.map +1 -0
  456. package/dist/server/modules/prompt/prompt.repository.js +522 -0
  457. package/dist/server/modules/prompt/prompt.repository.js.map +1 -0
  458. package/dist/server/modules/prompt/prompt.service.d.ts +46 -0
  459. package/dist/server/modules/prompt/prompt.service.d.ts.map +1 -0
  460. package/dist/server/modules/prompt/prompt.service.js +508 -0
  461. package/dist/server/modules/prompt/prompt.service.js.map +1 -0
  462. package/dist/server/modules/quick-start/quick-start.controller.d.ts +75 -0
  463. package/dist/server/modules/quick-start/quick-start.controller.d.ts.map +1 -0
  464. package/dist/server/modules/quick-start/quick-start.controller.js +86 -0
  465. package/dist/server/modules/quick-start/quick-start.controller.js.map +1 -0
  466. package/dist/server/modules/quick-start/quick-start.module.d.ts +3 -0
  467. package/dist/server/modules/quick-start/quick-start.module.d.ts.map +1 -0
  468. package/dist/server/modules/quick-start/quick-start.module.js +27 -0
  469. package/dist/server/modules/quick-start/quick-start.module.js.map +1 -0
  470. package/dist/server/modules/quick-start/quick-start.service.d.ts +77 -0
  471. package/dist/server/modules/quick-start/quick-start.service.d.ts.map +1 -0
  472. package/dist/server/modules/quick-start/quick-start.service.js +130 -0
  473. package/dist/server/modules/quick-start/quick-start.service.js.map +1 -0
  474. package/dist/server/modules/release-line/release-line.controller.d.ts +749 -0
  475. package/dist/server/modules/release-line/release-line.controller.d.ts.map +1 -0
  476. package/dist/server/modules/release-line/release-line.controller.js +108 -0
  477. package/dist/server/modules/release-line/release-line.controller.js.map +1 -0
  478. package/dist/server/modules/release-line/release-line.module.d.ts +3 -0
  479. package/dist/server/modules/release-line/release-line.module.d.ts.map +1 -0
  480. package/dist/server/modules/release-line/release-line.module.js +31 -0
  481. package/dist/server/modules/release-line/release-line.module.js.map +1 -0
  482. package/dist/server/modules/release-line/release-line.repository.d.ts +89 -0
  483. package/dist/server/modules/release-line/release-line.repository.d.ts.map +1 -0
  484. package/dist/server/modules/release-line/release-line.repository.js +834 -0
  485. package/dist/server/modules/release-line/release-line.repository.js.map +1 -0
  486. package/dist/server/modules/release-line/release-line.service.d.ts +109 -0
  487. package/dist/server/modules/release-line/release-line.service.d.ts.map +1 -0
  488. package/dist/server/modules/release-line/release-line.service.js +287 -0
  489. package/dist/server/modules/release-line/release-line.service.js.map +1 -0
  490. package/dist/server/modules/release-line/release-runner.repository.d.ts +89 -0
  491. package/dist/server/modules/release-line/release-runner.repository.d.ts.map +1 -0
  492. package/dist/server/modules/release-line/release-runner.repository.js +449 -0
  493. package/dist/server/modules/release-line/release-runner.repository.js.map +1 -0
  494. package/dist/server/modules/release-line/release-runner.service.d.ts +40 -0
  495. package/dist/server/modules/release-line/release-runner.service.d.ts.map +1 -0
  496. package/dist/server/modules/release-line/release-runner.service.js +417 -0
  497. package/dist/server/modules/release-line/release-runner.service.js.map +1 -0
  498. package/dist/server/modules/run-result/run-result.controller.d.ts +129 -0
  499. package/dist/server/modules/run-result/run-result.controller.d.ts.map +1 -0
  500. package/dist/server/modules/run-result/run-result.controller.js +103 -0
  501. package/dist/server/modules/run-result/run-result.controller.js.map +1 -0
  502. package/dist/server/modules/run-result/run-result.module.d.ts +3 -0
  503. package/dist/server/modules/run-result/run-result.module.d.ts.map +1 -0
  504. package/dist/server/modules/run-result/run-result.module.js +26 -0
  505. package/dist/server/modules/run-result/run-result.module.js.map +1 -0
  506. package/dist/server/modules/run-result/run-result.repository.d.ts +27 -0
  507. package/dist/server/modules/run-result/run-result.repository.d.ts.map +1 -0
  508. package/dist/server/modules/run-result/run-result.repository.js +521 -0
  509. package/dist/server/modules/run-result/run-result.repository.js.map +1 -0
  510. package/dist/server/modules/run-result/run-result.service.d.ts +22 -0
  511. package/dist/server/modules/run-result/run-result.service.d.ts.map +1 -0
  512. package/dist/server/modules/run-result/run-result.service.js +61 -0
  513. package/dist/server/modules/run-result/run-result.service.js.map +1 -0
  514. package/dist/server/modules/token/token.controller.d.ts +57 -0
  515. package/dist/server/modules/token/token.controller.d.ts.map +1 -0
  516. package/dist/server/modules/token/token.controller.js +99 -0
  517. package/dist/server/modules/token/token.controller.js.map +1 -0
  518. package/dist/server/modules/token/token.module.d.ts +3 -0
  519. package/dist/server/modules/token/token.module.d.ts.map +1 -0
  520. package/dist/server/modules/token/token.module.js +22 -0
  521. package/dist/server/modules/token/token.module.js.map +1 -0
  522. package/dist/server/modules/token/token.repository.d.ts +264 -0
  523. package/dist/server/modules/token/token.repository.d.ts.map +1 -0
  524. package/dist/server/modules/token/token.repository.js +97 -0
  525. package/dist/server/modules/token/token.repository.js.map +1 -0
  526. package/dist/server/modules/token/token.service.d.ts +23 -0
  527. package/dist/server/modules/token/token.service.d.ts.map +1 -0
  528. package/dist/server/modules/token/token.service.js +124 -0
  529. package/dist/server/modules/token/token.service.js.map +1 -0
  530. package/dist/server/proofhound-server.module.d.ts +7 -0
  531. package/dist/server/proofhound-server.module.d.ts.map +1 -0
  532. package/dist/server/proofhound-server.module.js +72 -0
  533. package/dist/server/proofhound-server.module.js.map +1 -0
  534. package/dist/shared/config/config.module.d.ts +3 -0
  535. package/dist/shared/config/config.module.d.ts.map +1 -0
  536. package/dist/shared/config/config.module.js +33 -0
  537. package/dist/shared/config/config.module.js.map +1 -0
  538. package/dist/shared/crypto/crypto.module.d.ts +3 -0
  539. package/dist/shared/crypto/crypto.module.d.ts.map +1 -0
  540. package/dist/shared/crypto/crypto.module.js +24 -0
  541. package/dist/shared/crypto/crypto.module.js.map +1 -0
  542. package/dist/shared/crypto/crypto.service.d.ts +9 -0
  543. package/dist/shared/crypto/crypto.service.d.ts.map +1 -0
  544. package/dist/shared/crypto/crypto.service.js +37 -0
  545. package/dist/shared/crypto/crypto.service.js.map +1 -0
  546. package/dist/shared/database/database.constants.d.ts +2 -0
  547. package/dist/shared/database/database.constants.d.ts.map +1 -0
  548. package/dist/shared/database/database.constants.js +5 -0
  549. package/dist/shared/database/database.constants.js.map +1 -0
  550. package/dist/shared/database/database.module.d.ts +3 -0
  551. package/dist/shared/database/database.module.d.ts.map +1 -0
  552. package/dist/shared/database/database.module.js +27 -0
  553. package/dist/shared/database/database.module.js.map +1 -0
  554. package/dist/shared/filters/pino-exception.filter.d.ts +9 -0
  555. package/dist/shared/filters/pino-exception.filter.d.ts.map +1 -0
  556. package/dist/shared/filters/pino-exception.filter.js +58 -0
  557. package/dist/shared/filters/pino-exception.filter.js.map +1 -0
  558. package/dist/shared/health/health.controller.d.ts +11 -0
  559. package/dist/shared/health/health.controller.d.ts.map +1 -0
  560. package/dist/shared/health/health.controller.js +51 -0
  561. package/dist/shared/health/health.controller.js.map +1 -0
  562. package/dist/shared/health/health.service.d.ts +25 -0
  563. package/dist/shared/health/health.service.d.ts.map +1 -0
  564. package/dist/shared/health/health.service.js +74 -0
  565. package/dist/shared/health/health.service.js.map +1 -0
  566. package/dist/shared/llm/runtime-limits.d.ts +4 -0
  567. package/dist/shared/llm/runtime-limits.d.ts.map +1 -0
  568. package/dist/shared/llm/runtime-limits.js +23 -0
  569. package/dist/shared/llm/runtime-limits.js.map +1 -0
  570. package/dist/shared/redis/redis-mutex.service.d.ts +20 -0
  571. package/dist/shared/redis/redis-mutex.service.d.ts.map +1 -0
  572. package/dist/shared/redis/redis-mutex.service.js +75 -0
  573. package/dist/shared/redis/redis-mutex.service.js.map +1 -0
  574. package/dist/shared/redis/redis.constants.d.ts +3 -0
  575. package/dist/shared/redis/redis.constants.d.ts.map +1 -0
  576. package/dist/shared/redis/redis.constants.js +6 -0
  577. package/dist/shared/redis/redis.constants.js.map +1 -0
  578. package/dist/shared/redis/redis.module.d.ts +8 -0
  579. package/dist/shared/redis/redis.module.d.ts.map +1 -0
  580. package/dist/shared/redis/redis.module.js +54 -0
  581. package/dist/shared/redis/redis.module.js.map +1 -0
  582. package/dist/shared/runtime-module-options.d.ts +5 -0
  583. package/dist/shared/runtime-module-options.d.ts.map +1 -0
  584. package/dist/shared/runtime-module-options.js +3 -0
  585. package/dist/shared/runtime-module-options.js.map +1 -0
  586. package/dist/webhook/channels/webhook/local-connector-context.resolver.d.ts +8 -0
  587. package/dist/webhook/channels/webhook/local-connector-context.resolver.d.ts.map +1 -0
  588. package/dist/webhook/channels/webhook/local-connector-context.resolver.js +63 -0
  589. package/dist/webhook/channels/webhook/local-connector-context.resolver.js.map +1 -0
  590. package/dist/webhook/channels/webhook/webhook-token.util.d.ts +4 -0
  591. package/dist/webhook/channels/webhook/webhook-token.util.d.ts.map +1 -0
  592. package/dist/webhook/channels/webhook/webhook-token.util.js +26 -0
  593. package/dist/webhook/channels/webhook/webhook-token.util.js.map +1 -0
  594. package/dist/webhook/channels/webhook/webhook.controller.d.ts +226 -0
  595. package/dist/webhook/channels/webhook/webhook.controller.d.ts.map +1 -0
  596. package/dist/webhook/channels/webhook/webhook.controller.js +121 -0
  597. package/dist/webhook/channels/webhook/webhook.controller.js.map +1 -0
  598. package/dist/webhook/channels/webhook/webhook.module.d.ts +3 -0
  599. package/dist/webhook/channels/webhook/webhook.module.d.ts.map +1 -0
  600. package/dist/webhook/channels/webhook/webhook.module.js +27 -0
  601. package/dist/webhook/channels/webhook/webhook.module.js.map +1 -0
  602. package/dist/webhook/channels/webhook/webhook.repository.d.ts +85 -0
  603. package/dist/webhook/channels/webhook/webhook.repository.d.ts.map +1 -0
  604. package/dist/webhook/channels/webhook/webhook.repository.js +268 -0
  605. package/dist/webhook/channels/webhook/webhook.repository.js.map +1 -0
  606. package/dist/webhook/channels/webhook/webhook.service.d.ts +144 -0
  607. package/dist/webhook/channels/webhook/webhook.service.d.ts.map +1 -0
  608. package/dist/webhook/channels/webhook/webhook.service.js +494 -0
  609. package/dist/webhook/channels/webhook/webhook.service.js.map +1 -0
  610. package/dist/webhook/index.d.ts +4 -0
  611. package/dist/webhook/index.d.ts.map +1 -0
  612. package/dist/webhook/index.js +8 -0
  613. package/dist/webhook/index.js.map +1 -0
  614. package/dist/webhook/infrastructure/orchestration/bullmq.module.d.ts +3 -0
  615. package/dist/webhook/infrastructure/orchestration/bullmq.module.d.ts.map +1 -0
  616. package/dist/webhook/infrastructure/orchestration/bullmq.module.js +34 -0
  617. package/dist/webhook/infrastructure/orchestration/bullmq.module.js.map +1 -0
  618. package/dist/webhook/infrastructure/orchestration/bullmq.service.d.ts +10 -0
  619. package/dist/webhook/infrastructure/orchestration/bullmq.service.d.ts.map +1 -0
  620. package/dist/webhook/infrastructure/orchestration/bullmq.service.js +42 -0
  621. package/dist/webhook/infrastructure/orchestration/bullmq.service.js.map +1 -0
  622. package/dist/webhook/proofhound-webhook.module.d.ts +7 -0
  623. package/dist/webhook/proofhound-webhook.module.d.ts.map +1 -0
  624. package/dist/webhook/proofhound-webhook.module.js +31 -0
  625. package/dist/webhook/proofhound-webhook.module.js.map +1 -0
  626. package/dist/worker/config/worker-concurrency.d.ts +3 -0
  627. package/dist/worker/config/worker-concurrency.d.ts.map +1 -0
  628. package/dist/worker/config/worker-concurrency.js +10 -0
  629. package/dist/worker/config/worker-concurrency.js.map +1 -0
  630. package/dist/worker/consumers/llm.consumer.d.ts +29 -0
  631. package/dist/worker/consumers/llm.consumer.d.ts.map +1 -0
  632. package/dist/worker/consumers/llm.consumer.js +234 -0
  633. package/dist/worker/consumers/llm.consumer.js.map +1 -0
  634. package/dist/worker/consumers/probe.consumer.d.ts +16 -0
  635. package/dist/worker/consumers/probe.consumer.d.ts.map +1 -0
  636. package/dist/worker/consumers/probe.consumer.js +76 -0
  637. package/dist/worker/consumers/probe.consumer.js.map +1 -0
  638. package/dist/worker/index.d.ts +4 -0
  639. package/dist/worker/index.d.ts.map +1 -0
  640. package/dist/worker/index.js +9 -0
  641. package/dist/worker/index.js.map +1 -0
  642. package/dist/worker/infrastructure/llm/model-secret.provider.d.ts +4 -0
  643. package/dist/worker/infrastructure/llm/model-secret.provider.d.ts.map +1 -0
  644. package/dist/worker/infrastructure/llm/model-secret.provider.js +14 -0
  645. package/dist/worker/infrastructure/llm/model-secret.provider.js.map +1 -0
  646. package/dist/worker/proofhound-worker.module.d.ts +7 -0
  647. package/dist/worker/proofhound-worker.module.d.ts.map +1 -0
  648. package/dist/worker/proofhound-worker.module.js +44 -0
  649. package/dist/worker/proofhound-worker.module.js.map +1 -0
  650. package/dist/worker/runners/llm-runner.d.ts +42 -0
  651. package/dist/worker/runners/llm-runner.d.ts.map +1 -0
  652. package/dist/worker/runners/llm-runner.js +178 -0
  653. package/dist/worker/runners/llm-runner.js.map +1 -0
  654. package/dist/worker/runners/model-secret.d.ts +11 -0
  655. package/dist/worker/runners/model-secret.d.ts.map +1 -0
  656. package/dist/worker/runners/model-secret.js +14 -0
  657. package/dist/worker/runners/model-secret.js.map +1 -0
  658. package/dist/worker/runners/probe-runner.d.ts +22 -0
  659. package/dist/worker/runners/probe-runner.d.ts.map +1 -0
  660. package/dist/worker/runners/probe-runner.js +52 -0
  661. package/dist/worker/runners/probe-runner.js.map +1 -0
  662. package/dist/worker/runners/run-result-writer.d.ts +10 -0
  663. package/dist/worker/runners/run-result-writer.d.ts.map +1 -0
  664. package/dist/worker/runners/run-result-writer.js +84 -0
  665. package/dist/worker/runners/run-result-writer.js.map +1 -0
  666. package/dist/worker/scripts/probe-model-from-env.d.ts +2 -0
  667. package/dist/worker/scripts/probe-model-from-env.d.ts.map +1 -0
  668. package/dist/worker/scripts/probe-model-from-env.js +130 -0
  669. package/dist/worker/scripts/probe-model-from-env.js.map +1 -0
  670. package/package.json +93 -0
@@ -0,0 +1,1753 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.OptimizationService = void 0;
13
+ exports.collectPromptVersionIds = collectPromptVersionIds;
14
+ const common_1 = require("@nestjs/common");
15
+ const shared_1 = require("@proofhound/shared");
16
+ const logger_1 = require("@proofhound/logger");
17
+ const access_control_1 = require("../../common/access-control");
18
+ const access_control_service_1 = require("../../common/contracts/access-control.service");
19
+ const workflow_authorization_hook_1 = require("../../common/contracts/workflow-authorization.hook");
20
+ const db_error_1 = require("../../common/errors/db-error");
21
+ const experiment_aggregator_1 = require("../experiment/experiment.aggregator");
22
+ const experiment_repository_1 = require("../experiment/experiment.repository");
23
+ const experiment_service_1 = require("../experiment/experiment.service");
24
+ const prompt_repository_1 = require("../prompt/prompt.repository");
25
+ const run_result_service_1 = require("../run-result/run-result.service");
26
+ const optimization_launcher_1 = require("./optimization.launcher");
27
+ const optimization_workflow_1 = require("./optimization.workflow");
28
+ const optimization_repository_1 = require("./optimization.repository");
29
+ // Logger used internally by the detail-page aggregation helper; a separate binding makes it zero-cost in production after debug is disabled,
30
+ // from the same source as this.logger in the class (both are pino instances).
31
+ const detailHelperLogger = (0, logger_1.createLogger)('optimization.service.detail', { service: 'server' });
32
+ let OptimizationService = class OptimizationService {
33
+ constructor(repo, launcher, experimentRepo, experimentService, runResults, promptRepo, accessControl, workflowAuth) {
34
+ this.repo = repo;
35
+ this.launcher = launcher;
36
+ this.experimentRepo = experimentRepo;
37
+ this.experimentService = experimentService;
38
+ this.runResults = runResults;
39
+ this.promptRepo = promptRepo;
40
+ this.accessControl = accessControl;
41
+ this.workflowAuth = workflowAuth;
42
+ this.logger = (0, logger_1.createLogger)('optimization.service', { service: 'server' });
43
+ }
44
+ async listOptimizations(projectId, actor, query = {}) {
45
+ await this.getAccessibleProject(projectId, actor);
46
+ const allRows = await this.repo.listOptimizations(projectId);
47
+ // On demand, load round experiments + round_steps per optimization:
48
+ // - experiments populate trend (LiveCard sparkline)
49
+ // - round_steps allow the list to show the latest round and update time even during analysis/generation/child-experiment startup
50
+ const allItems = [];
51
+ for (const row of allRows) {
52
+ const [rounds, roundSteps] = await Promise.all([
53
+ this.repo.listRoundExperimentsForOptimization(row.id),
54
+ this.repo.listRoundStepsForOptimization(row.id),
55
+ ]);
56
+ const liveRounds = await this.withLiveRoundMetrics(rounds);
57
+ const liveProjection = deriveLiveProjection(row, liveRounds, roundSteps);
58
+ const { values: trend, hasBaseline } = this.deriveListTrend(row, liveRounds);
59
+ allItems.push(this.toListItem(row, {
60
+ trend,
61
+ trendHasBaseline: hasBaseline,
62
+ currentRound: liveProjection.currentRound,
63
+ updatedAt: liveProjection.updatedAt,
64
+ }));
65
+ }
66
+ const filtered = this.filterItems(allItems, query);
67
+ const data = this.sortItems(filtered, query.sort);
68
+ return { data, total: data.length };
69
+ }
70
+ async getOptimization(projectId, optimizationId, actor) {
71
+ await this.getAccessibleProject(projectId, actor);
72
+ const row = await this.repo.findOptimizationById(projectId, optimizationId);
73
+ if (!row) {
74
+ throw new common_1.NotFoundException(`Optimization ${optimizationId} not found`);
75
+ }
76
+ // Fetch concurrently: experiments + LLM run_results + round_steps, then merge and feed into toDetail.
77
+ const [rounds, llmRows, roundSteps] = await Promise.all([
78
+ this.repo.listRoundExperimentsForOptimization(optimizationId),
79
+ this.repo.listOptimizationLlmRunResults(optimizationId),
80
+ this.repo.listRoundStepsForOptimization(optimizationId),
81
+ ]);
82
+ // For running rounds, add a layer of live aggregate between batch aggregation steps (mirroring ExperimentService.withLiveMetrics),
83
+ // so that the detail page's 5-second refresh of the progress bar / quality metrics advances with run_results in real time, rather than being stuck on the last batch-written snapshot.
84
+ const liveRounds = await this.withLiveRoundMetrics(rounds);
85
+ const versionIds = collectPromptVersionIds(row, liveRounds, llmRows);
86
+ const promptBodyMap = await this.repo.loadPromptVersionsByIds(versionIds);
87
+ return this.toDetail(row, { rounds: liveRounds, llmRows, roundSteps, promptBodyMap });
88
+ }
89
+ // Isomorphic to ExperimentService.withLiveMetrics: only triggered for running rounds; aggregates from ph_runs.run_results in real time
90
+ // to override processedSamples / failedSamples / metrics, so deriveTrendSeries / deriveRoundDetails /
91
+ // buildExperimentResult / goalChips all move along. Empty aggregate (no terminal row yet in run_results) → keep the snapshot,
92
+ // to avoid regressing progress to 0/null. Terminal rounds are untouched and continue to read the snapshot, to avoid a GROUP BY on every GET.
93
+ async withLiveRoundMetrics(rounds) {
94
+ if (rounds.length === 0)
95
+ return rounds;
96
+ return Promise.all(rounds.map(async (round) => {
97
+ if (round.status !== 'running' || !round.experimentId)
98
+ return round;
99
+ const [aggRows, latency] = await Promise.all([
100
+ this.runResults.aggregateExperiment(round.experimentId),
101
+ this.runResults.aggregateExperimentLatency(round.experimentId),
102
+ ]);
103
+ const live = (0, experiment_aggregator_1.aggregateExperimentMetrics)(aggRows, latency);
104
+ // Empty aggregate (no terminal row yet in run_results) → keep the experiments snapshot to avoid regressing progress to 0/null
105
+ if (live.totalCount === 0)
106
+ return round;
107
+ return {
108
+ ...round,
109
+ processedSamples: live.totalCount,
110
+ failedSamples: live.failedCount,
111
+ metrics: live.metrics,
112
+ };
113
+ }));
114
+ }
115
+ async createOptimization(projectId, body, actor, source = 'api',
116
+ // orgId (SaaS-only; undefined in OSS) is sourced from the resolved ProjectContext — the project's org is the
117
+ // rate-limit bucket (SPEC 08 §3.7). Threaded into launcher.launch → runWorkflow → snapshot.orgId so the worker
118
+ // composes an org-scoped limiter key; child-experiment launches inside the workflow inherit it from the snapshot.
119
+ orgId) {
120
+ await this.getWritableProject(projectId, actor);
121
+ const existing = await this.repo.findOptimizationByProjectAndName(projectId, body.name);
122
+ if (existing) {
123
+ throw new common_1.ConflictException('optimization_name_taken');
124
+ }
125
+ let resolvedPromptId = body.promptId ?? null;
126
+ let resolvedBaseVersionId = body.baseVersionId ?? null;
127
+ const requestedPromptLanguage = body.promptLanguage ?? shared_1.DEFAULT_PROMPT_LANGUAGE;
128
+ let workflowStartAuthorized = false;
129
+ const assertWorkflowStart = async () => {
130
+ if (workflowStartAuthorized)
131
+ return;
132
+ await this.workflowAuth.assertCanStart((0, access_control_1.toActorContext)(actor), { projectId, orgId, source: 'local' }, 'optimization');
133
+ workflowStartAuthorized = true;
134
+ };
135
+ if (body.startingMode === 'from_experiment' &&
136
+ body.sourceExperimentId &&
137
+ (!resolvedPromptId || !resolvedBaseVersionId)) {
138
+ const sourceExperiment = await this.experimentRepo.findExperimentById(projectId, body.sourceExperimentId);
139
+ if (!sourceExperiment) {
140
+ throw new common_1.BadRequestException(`Source experiment ${body.sourceExperimentId} not found`);
141
+ }
142
+ resolvedBaseVersionId ??= sourceExperiment.promptVersionId;
143
+ resolvedPromptId ??= sourceExperiment.promptId;
144
+ }
145
+ // SPEC 25 §2: the baseline version for the from_prompt_version start is auto-selected by the system
146
+ // Prefer prompts.current_online_version_id; otherwise take the latest version number of this prompt.
147
+ if (body.startingMode === 'from_prompt_version' && resolvedPromptId && !resolvedBaseVersionId) {
148
+ resolvedBaseVersionId = await this.repo.findActiveVersionIdForPrompt(resolvedPromptId);
149
+ if (!resolvedBaseVersionId) {
150
+ throw new common_1.BadRequestException(`Prompt ${resolvedPromptId} has no usable version for optimization`);
151
+ }
152
+ }
153
+ // SPEC 25 §2.1: for from_dataset_only start, auto-create an empty prompt as the carrier entity;
154
+ // baseVersionId stays null until workflow.generateFirstVersionStep backfills it.
155
+ if (body.startingMode === 'from_dataset_only' && !resolvedPromptId) {
156
+ if (!body.analysisModelId) {
157
+ throw new common_1.BadRequestException('analysis_model_required_for_dataset_only_starting_mode');
158
+ }
159
+ if (resolvedBaseVersionId) {
160
+ throw new common_1.BadRequestException('base_version_must_be_unset_for_dataset_only_starting_mode');
161
+ }
162
+ const dataset = await this.repo.findDatasetForOptimization(projectId, body.datasetId);
163
+ if (!dataset) {
164
+ throw new common_1.BadRequestException(`Dataset ${body.datasetId} not found`);
165
+ }
166
+ await assertWorkflowStart();
167
+ resolvedPromptId = await this.createPlaceholderPromptWithRetry({
168
+ projectId,
169
+ datasetName: dataset.name,
170
+ optimizationName: body.name,
171
+ datasetId: body.datasetId,
172
+ promptLanguage: requestedPromptLanguage,
173
+ createdBy: actor.sub,
174
+ });
175
+ }
176
+ const resolvedPromptLanguage = await this.resolvePromptLanguage(body.promptLanguage, resolvedBaseVersionId);
177
+ await assertWorkflowStart();
178
+ const insertedId = await this.insertOptimizationOrThrowNameConflict({
179
+ projectId,
180
+ name: body.name,
181
+ description: body.description ?? null,
182
+ optimizationHint: normalizeOptimizationHint(body.optimizationHint),
183
+ strategy: body.strategy,
184
+ strategyConfig: body.strategyConfig ?? {},
185
+ startingMode: body.startingMode,
186
+ sourceExperimentId: body.sourceExperimentId ?? null,
187
+ promptId: resolvedPromptId,
188
+ baseVersionId: resolvedBaseVersionId,
189
+ datasetId: body.datasetId,
190
+ experimentModelId: body.experimentModelId,
191
+ analysisModelId: body.analysisModelId,
192
+ promptLanguage: resolvedPromptLanguage,
193
+ status: 'running',
194
+ goals: body.goals,
195
+ fieldWhitelist: body.fieldWhitelist ?? null,
196
+ runConfig: body.runConfig ?? {},
197
+ maxRounds: body.loopLimits.maxRounds,
198
+ createdBy: actor.sub,
199
+ });
200
+ let workflowId = null;
201
+ try {
202
+ workflowId = await this.launcher.launch(insertedId, orgId);
203
+ }
204
+ catch (error) {
205
+ const reason = `launch_failed: ${error.message}`;
206
+ const now = new Date();
207
+ await this.repo.updateOptimization(projectId, insertedId, {
208
+ status: 'failed',
209
+ controlState: null,
210
+ finishedAt: now,
211
+ summary: { kind: 'failed', reason, finalizedAt: now.toISOString() },
212
+ });
213
+ throw error;
214
+ }
215
+ const inserted = await this.repo.findOptimizationById(projectId, insertedId);
216
+ if (!inserted) {
217
+ throw new common_1.NotFoundException(`Optimization ${insertedId} not found after insert`);
218
+ }
219
+ return this.toListItem(inserted);
220
+ }
221
+ async resolvePromptLanguage(requested, baseVersionId) {
222
+ if (requested)
223
+ return requested;
224
+ if (!baseVersionId)
225
+ return shared_1.DEFAULT_PROMPT_LANGUAGE;
226
+ const stored = await this.repo.findPromptVersionLanguage(baseVersionId);
227
+ const parsed = shared_1.promptLanguageSchema.safeParse(stored);
228
+ return parsed.success ? parsed.data : shared_1.DEFAULT_PROMPT_LANGUAGE;
229
+ }
230
+ async insertOptimizationOrThrowNameConflict(input) {
231
+ try {
232
+ return await this.repo.insertOptimization(input);
233
+ }
234
+ catch (error) {
235
+ if (isOptimizationNameUniqueViolation(error)) {
236
+ throw new common_1.ConflictException('optimization_name_taken');
237
+ }
238
+ throw error;
239
+ }
240
+ }
241
+ // SPEC 25 §2.1: in from_dataset_only mode, create an empty prompt to carry the first version.
242
+ // Naming rule `<localized-prefix>-${datasetName}-${ISO time down to the minute}`; on prompts_project_name_unique collision,
243
+ // retry once with an 8-char hash suffix appended; a second collision → throw prompt_name_collision_v1.
244
+ async createPlaceholderPromptWithRetry(input) {
245
+ const baseName = buildOptimizationPromptName(input.datasetName, new Date(), input.promptLanguage);
246
+ try {
247
+ return await this.promptRepo.createPlaceholderPromptForOptimization({
248
+ projectId: input.projectId,
249
+ name: baseName,
250
+ defaultDatasetId: input.datasetId,
251
+ createdBy: input.createdBy,
252
+ });
253
+ }
254
+ catch (err) {
255
+ if (!isPromptNameUniqueViolation(err))
256
+ throw err;
257
+ const suffix = `-${shortHash(`${input.optimizationName}|${Date.now()}`)}`;
258
+ try {
259
+ return await this.promptRepo.createPlaceholderPromptForOptimization({
260
+ projectId: input.projectId,
261
+ name: `${baseName}${suffix}`,
262
+ defaultDatasetId: input.datasetId,
263
+ createdBy: input.createdBy,
264
+ });
265
+ }
266
+ catch (err2) {
267
+ if (isPromptNameUniqueViolation(err2)) {
268
+ throw new common_1.ConflictException('prompt_name_collision_v1');
269
+ }
270
+ throw err2;
271
+ }
272
+ }
273
+ }
274
+ async controlOptimization(projectId, optimizationId, action, actor, source = 'api',
275
+ // orgId (SaaS-only; undefined in OSS) sourced from the resolved ProjectContext — the project's org is the
276
+ // rate-limit bucket (SPEC 08 §3.7). Threaded into launcher.resume on the resume path.
277
+ orgId) {
278
+ await this.getWritableProject(projectId, actor);
279
+ const parsedAction = shared_1.optimizationControlActionSchema.parse(action);
280
+ const current = await this.repo.findOptimizationById(projectId, optimizationId);
281
+ if (!current) {
282
+ throw new common_1.NotFoundException(`Optimization ${optimizationId} not found`);
283
+ }
284
+ const patch = this.getControlPatch(parsedAction, current);
285
+ await this.repo.updateOptimization(projectId, optimizationId, patch);
286
+ // SPEC 25 §7 dual-path linkage: on stop/cancel, immediately call controlExperiment on the child; do not block the parent control write to DB
287
+ // resume is not linked here — child experiment resume is handled by the workflow in the isResumeRound branch via controlChildExperimentStep
288
+ if (parsedAction === 'stop' || parsedAction === 'cancel') {
289
+ await this.tryLinkChildExperimentControl(optimizationId, parsedAction);
290
+ }
291
+ if (parsedAction === 'resume') {
292
+ await this.workflowAuth.assertCanStart((0, access_control_1.toActorContext)(actor), { projectId, orgId, source: 'local' }, 'optimization');
293
+ await this.launcher.resume(optimizationId, orgId);
294
+ }
295
+ const updated = await this.repo.findOptimizationById(projectId, optimizationId);
296
+ if (!updated) {
297
+ throw new common_1.NotFoundException(`Optimization ${optimizationId} not found after update`);
298
+ }
299
+ return this.toListItem(updated);
300
+ }
301
+ // SPEC 25 §7: stop/cancel on the parent optimization immediately propagates to the active child experiment. Best-effort:
302
+ // - no active child experiment (already terminal / not yet created) → no-op
303
+ // - service throws Conflict / NotFound → warn and swallow (the workflow poll is the backstop)
304
+ // - other errors → warn and swallow (do not throw, to avoid blocking the parent control_state write)
305
+ async tryLinkChildExperimentControl(optimizationId, action) {
306
+ const activeChild = await this.repo.findActiveChildExperiment(optimizationId);
307
+ if (!activeChild)
308
+ return;
309
+ try {
310
+ await this.experimentService.controlExperiment(activeChild.projectId, activeChild.id, action, optimization_workflow_1.SYSTEM_ACTOR_OPTIMIZATION, 'system');
311
+ this.logger.info({
312
+ optimizationId,
313
+ childExperimentId: activeChild.id,
314
+ roundIndex: activeChild.roundIndex,
315
+ action,
316
+ }, 'optimization_child_control_linked_from_service');
317
+ }
318
+ catch (error) {
319
+ const level = error instanceof common_1.ConflictException || error instanceof common_1.NotFoundException ? 'skipped' : 'failed';
320
+ this.logger.warn({
321
+ optimizationId,
322
+ childExperimentId: activeChild.id,
323
+ action,
324
+ err: error.message,
325
+ }, `optimization_child_control_${level}`);
326
+ }
327
+ }
328
+ async deleteOptimization(projectId, optimizationId, actor, source = 'api') {
329
+ void source;
330
+ await this.getWritableProject(projectId, actor);
331
+ const row = await this.repo.findOptimizationById(projectId, optimizationId);
332
+ if (!row) {
333
+ throw new common_1.NotFoundException(`Optimization ${optimizationId} not found`);
334
+ }
335
+ await this.repo.hardDeleteOptimization(projectId, optimizationId);
336
+ }
337
+ // ----------------------------- helpers -----------------------------
338
+ async getAccessibleProject(projectId, actor) {
339
+ await this.accessControl.assertCan((0, access_control_1.toActorContext)(actor), { projectId, source: 'local' }, 'project_read');
340
+ const project = await this.repo.findProjectAccess(actor.sub, projectId, actor.isSuperAdmin);
341
+ if (!project) {
342
+ throw new common_1.NotFoundException(`Project ${projectId} not found`);
343
+ }
344
+ return project;
345
+ }
346
+ async getWritableProject(projectId, actor) {
347
+ await this.accessControl.assertCan((0, access_control_1.toActorContext)(actor), { projectId, source: 'local' }, 'project_write');
348
+ return this.getAccessibleProject(projectId, actor);
349
+ }
350
+ getControlPatch(action, row) {
351
+ const status = row.status;
352
+ const now = new Date();
353
+ if (action === 'stop') {
354
+ if (status !== 'running') {
355
+ throw new common_1.ConflictException('optimization_stop_invalid_status');
356
+ }
357
+ // Preemptive terminal-state write: set status='stopped' + control_state='stop' + finished_at in one shot.
358
+ // The workflow will call finalize once the current LLM step finishes, but repo.finalize has a status='running' guard,
359
+ // so the second write is skipped — avoiding overwriting the terminal state already written by service, and avoiding finished_at drift.
360
+ // control_state is preserved; on the next round the workflow reads status as terminal and exits directly.
361
+ return {
362
+ status: 'stopped',
363
+ controlState: 'stop',
364
+ finishedAt: now,
365
+ updatedAt: now,
366
+ };
367
+ }
368
+ if (action === 'resume') {
369
+ if (status !== 'stopped') {
370
+ throw new common_1.ConflictException('optimization_resume_invalid_status');
371
+ }
372
+ return {
373
+ status: 'running',
374
+ controlState: 'resume',
375
+ startedAt: row.startedAt ?? now,
376
+ finishedAt: null,
377
+ updatedAt: now,
378
+ };
379
+ }
380
+ if (status === 'success' || status === 'cancelled') {
381
+ throw new common_1.ConflictException('optimization_cancel_invalid_status');
382
+ }
383
+ // cancel is preemptive in the same way (regardless of whether the original status was running or stopped/failed).
384
+ return {
385
+ status: 'cancelled',
386
+ controlState: 'cancel',
387
+ finishedAt: now,
388
+ updatedAt: now,
389
+ };
390
+ }
391
+ toListItem(row, options = {}) {
392
+ return {
393
+ trend: options.trend ?? null,
394
+ trendHasBaseline: options.trendHasBaseline ?? false,
395
+ id: row.id,
396
+ projectId: row.projectId,
397
+ name: row.name,
398
+ description: row.description,
399
+ strategy: row.strategy,
400
+ startingMode: row.startingMode,
401
+ status: row.status,
402
+ controlState: row.controlState,
403
+ sourceExperimentId: row.sourceExperimentId,
404
+ sourceExperimentName: row.sourceExperimentName,
405
+ promptId: row.promptId,
406
+ promptName: row.promptName,
407
+ baseVersionId: row.baseVersionId,
408
+ baseVersionNumber: row.baseVersionNumber,
409
+ datasetId: row.datasetId,
410
+ datasetName: row.datasetName,
411
+ datasetSamples: row.datasetSamples,
412
+ experimentModelId: row.experimentModelId,
413
+ experimentModelName: row.experimentModelName,
414
+ analysisModelId: row.analysisModelId,
415
+ analysisModelName: row.analysisModelName,
416
+ promptLanguage: this.parsePromptLanguage(row.promptLanguage),
417
+ goals: this.parseGoals(row.goals),
418
+ fieldWhitelist: this.parseFieldWhitelist(row.fieldWhitelist),
419
+ runConfig: this.parseRunConfig(row.runConfig),
420
+ maxRounds: row.maxRounds,
421
+ currentRound: options.currentRound ?? row.currentRound,
422
+ bestVersionId: row.bestVersionId,
423
+ bestVersionNumber: row.bestVersionNumber,
424
+ bestMetrics: this.parseBestMetrics(row.bestMetrics),
425
+ summary: this.parseSummary(row.summary),
426
+ analysisFailureReason: this.parseAnalysisFailureReason(row.analysisFailureReason),
427
+ dbosWorkflowId: row.dbosWorkflowId,
428
+ createdBy: row.createdBy,
429
+ createdByDisplayName: row.createdByDisplayName,
430
+ createdByUsername: row.createdByUsername,
431
+ startedAt: row.startedAt?.toISOString() ?? null,
432
+ finishedAt: row.finishedAt?.toISOString() ?? null,
433
+ createdAt: row.createdAt.toISOString(),
434
+ updatedAt: (options.updatedAt ?? row.updatedAt).toISOString(),
435
+ deletedAt: row.deletedAt?.toISOString() ?? null,
436
+ };
437
+ }
438
+ toDetail(row, aggregation = { rounds: [], llmRows: [], roundSteps: [] }) {
439
+ const roundSteps = aggregation.roundSteps ?? [];
440
+ const hasSourceMetrics = row.sourceExperimentMetrics &&
441
+ typeof row.sourceExperimentMetrics === 'object' &&
442
+ Object.keys(row.sourceExperimentMetrics).length > 0;
443
+ // Real data sources: experiments ∪ round_steps ∪ source experiment metrics (baseline). If any is non-empty, take the real-aggregate path.
444
+ // The presence of round_steps means that as soon as prepareRoundImpl starts (error_analysis=running),
445
+ // the round is visible on the detail page without waiting for the child experiment to be created;
446
+ // when baseline metrics exist, even round 0 takes the real-aggregate path so the metrics trend card immediately shows the baseline first point.
447
+ const realRounds = aggregation.rounds.length > 0 || roundSteps.length > 0 || hasSourceMetrics;
448
+ this.logger.debug({
449
+ optimizationId: row.id,
450
+ status: row.status,
451
+ roundsLen: aggregation.rounds.length,
452
+ llmRowsLen: aggregation.llmRows.length,
453
+ roundStepsLen: roundSteps.length,
454
+ llmRowsBySource: aggregation.llmRows.reduce((acc, r) => {
455
+ acc[r.source] = (acc[r.source] ?? 0) + 1;
456
+ return acc;
457
+ }, {}),
458
+ realRounds,
459
+ }, 'optimization_detail_aggregate_start');
460
+ const liveProjection = deriveLiveProjection(row, aggregation.rounds, roundSteps);
461
+ const listTrend = this.deriveListTrend(row, aggregation.rounds);
462
+ const base = this.toListItem(row, {
463
+ trend: listTrend.values,
464
+ trendHasBaseline: listTrend.hasBaseline,
465
+ currentRound: liveProjection.currentRound,
466
+ updatedAt: liveProjection.updatedAt,
467
+ });
468
+ const ownerHandle = row.createdByUsername ? `@${row.createdByUsername}` : (row.createdByDisplayName ?? '—');
469
+ const startedAt = row.startedAt;
470
+ const finishedAt = row.finishedAt;
471
+ const elapsedMs = startedAt ? Math.max(0, (finishedAt ?? new Date()).getTime() - startedAt.getTime()) : null;
472
+ const goalScope = this.deriveGoalScope(base.goals);
473
+ const goalsLines = this.deriveGoalsLines(base.goals);
474
+ const experimentConfig = this.deriveExperimentConfig(row, base.runConfig);
475
+ const iterationConfig = this.deriveIterationConfig(row, base.runConfig);
476
+ const sourceMetrics = row.sourceExperimentMetrics && typeof row.sourceExperimentMetrics === 'object'
477
+ ? row.sourceExperimentMetrics
478
+ : null;
479
+ const derivedBaseline = this.deriveBaseline(row, sourceMetrics, aggregation.promptBodyMap ?? new Map());
480
+ const derivedBestRoundLabel = row.bestVersionNumber ? `v${row.bestVersionNumber}` : null;
481
+ if (realRounds) {
482
+ // Real aggregate
483
+ const trend = this.deriveTrendSeries(base.goals, aggregation.rounds, sourceMetrics, base.startingMode);
484
+ const rounds = this.deriveRoundDetails(aggregation.rounds, aggregation.llmRows, roundSteps, aggregation.promptBodyMap ?? new Map(), row.baseVersionId, base.goals, sourceMetrics, base.startingMode);
485
+ const effectiveBest = this.deriveEffectiveBest(row, aggregation.rounds, base.goals, base.bestMetrics, sourceMetrics, base.startingMode);
486
+ const goalProgress = this.deriveGoalProgress(base.goals, effectiveBest?.metrics ?? null);
487
+ const bestVersion = this.deriveBestVersion(row, effectiveBest);
488
+ const bestRoundLabel = bestVersion ? bestVersion.generatedAtRoundLabel : derivedBestRoundLabel;
489
+ const baseline = derivedBaseline;
490
+ return {
491
+ ...base,
492
+ optimizationHint: normalizeOptimizationHint(row.optimizationHint),
493
+ ownerHandle,
494
+ elapsedMs,
495
+ experimentConfig,
496
+ iterationConfig,
497
+ goalScope,
498
+ goalsLines,
499
+ controlStrip: null,
500
+ trend,
501
+ trendBaselineRef: null,
502
+ bestRoundLabel,
503
+ rounds,
504
+ baseline,
505
+ goalProgress,
506
+ bestVersion,
507
+ };
508
+ }
509
+ // No real data → fall back to the dev mock (dev seed for demo / e2e)
510
+ const mock = this.extractDevMockTimeline(base.runConfig);
511
+ this.logger.debug({
512
+ optimizationId: row.id,
513
+ reason: 'no_real_rounds',
514
+ hasDevMock: !!mock,
515
+ mockRoundsLen: mock?.rounds?.length ?? 0,
516
+ }, 'optimization_detail_using_dev_mock');
517
+ const baseline = derivedBaseline && mock?.baselineMetrics
518
+ ? { ...derivedBaseline, metrics: mock.baselineMetrics }
519
+ : derivedBaseline;
520
+ return {
521
+ ...base,
522
+ optimizationHint: normalizeOptimizationHint(row.optimizationHint),
523
+ ownerHandle,
524
+ elapsedMs,
525
+ experimentConfig,
526
+ iterationConfig,
527
+ goalScope,
528
+ goalsLines,
529
+ controlStrip: mock?.controlStrip ?? null,
530
+ trend: mock?.trend ?? [],
531
+ trendBaselineRef: mock?.trendBaselineRef ?? null,
532
+ bestRoundLabel: mock?.bestRoundLabel ?? derivedBestRoundLabel,
533
+ rounds: mock?.rounds ?? [],
534
+ baseline,
535
+ goalProgress: mock?.goalProgress ?? [],
536
+ bestVersion: mock?.bestVersion ?? null,
537
+ };
538
+ }
539
+ deriveListTrend(row, rounds) {
540
+ const goals = this.parseGoals(row.goals);
541
+ const primary = goals[0]?.metric;
542
+ if (!primary)
543
+ return { values: null, hasBaseline: false };
544
+ const baselineMetrics = row.sourceExperimentMetrics && typeof row.sourceExperimentMetrics === 'object'
545
+ ? row.sourceExperimentMetrics
546
+ : null;
547
+ const sortedRounds = rounds.slice().sort((a, b) => a.roundIndex - b.roundIndex);
548
+ if (row.startingMode === 'from_dataset_only') {
549
+ const baselineRound = sortedRounds.find((r) => r.roundIndex === 0);
550
+ const baselineValue = extractMetric(baselineRound?.metrics ?? baselineMetrics, primary);
551
+ const roundValues = sortedRounds
552
+ .filter((r) => r.roundIndex > 0)
553
+ .map((r) => extractMetric(r.metrics, primary))
554
+ .filter((v) => v !== null);
555
+ const values = baselineValue !== null ? [baselineValue, ...roundValues] : roundValues;
556
+ return { values: values.length > 0 ? values : null, hasBaseline: baselineValue !== null };
557
+ }
558
+ const baselineValue = baselineMetrics ? extractMetric(baselineMetrics, primary) : null;
559
+ const hasBaseline = baselineValue !== null;
560
+ const roundValues = sortedRounds
561
+ .map((r) => extractMetric(r.metrics, primary))
562
+ .filter((v) => v !== null);
563
+ const values = hasBaseline ? [baselineValue, ...roundValues] : roundValues;
564
+ return { values: values.length > 0 ? values : null, hasBaseline };
565
+ }
566
+ deriveTrendSeries(goals, rounds, baselineMetrics = null, startingMode) {
567
+ const sorted = rounds.slice().sort((a, b) => a.roundIndex - b.roundIndex);
568
+ const keyCandidates = [
569
+ { key: 'accuracy', metric: 'accuracy' },
570
+ { key: 'recall', metric: 'recall' },
571
+ { key: 'fpr', metric: 'fpr' },
572
+ ];
573
+ const series = [];
574
+ for (const { key, metric } of keyCandidates) {
575
+ const datasetBaselineRound = startingMode === 'from_dataset_only' ? sorted.find((r) => r.roundIndex === 0) : undefined;
576
+ const baselineValue = startingMode === 'from_dataset_only'
577
+ ? extractMetric(datasetBaselineRound?.metrics ?? baselineMetrics, metric)
578
+ : baselineMetrics
579
+ ? extractMetric(baselineMetrics, metric)
580
+ : null;
581
+ const optimizationRounds = startingMode === 'from_dataset_only' ? sorted.filter((r) => r.roundIndex > 0) : sorted;
582
+ const roundValues = optimizationRounds
583
+ .map((r) => extractMetric(r.metrics, metric))
584
+ .filter((v) => v !== null);
585
+ const hasBaseline = baselineValue !== null;
586
+ const values = hasBaseline ? [baselineValue, ...roundValues] : roundValues;
587
+ if (values.length === 0)
588
+ continue;
589
+ const matchingGoal = goals.find((g) => g.metric === metric);
590
+ // bestRoundIndex still points to the best index within the round set (excluding baseline) to preserve prop semantics
591
+ let bestRoundIndex;
592
+ if (roundValues.length > 0) {
593
+ let bestIdx = 0;
594
+ for (let i = 1; i < roundValues.length; i++) {
595
+ if (matchingGoal?.comparator === 'lte'
596
+ ? roundValues[i] < roundValues[bestIdx]
597
+ : roundValues[i] > roundValues[bestIdx]) {
598
+ bestIdx = i;
599
+ }
600
+ }
601
+ bestRoundIndex = bestIdx;
602
+ }
603
+ series.push({
604
+ key,
605
+ labelKey: `optimizations.metrics.${key}`,
606
+ betterIsLower: matchingGoal?.comparator === 'lte',
607
+ values,
608
+ target: matchingGoal?.target,
609
+ bestRoundIndex,
610
+ hasBaseline,
611
+ });
612
+ }
613
+ return series;
614
+ }
615
+ deriveRoundDetails(rounds, llmRows, roundSteps, promptBodyMap, baseVersionId, goals, baselineMetrics, startingMode) {
616
+ const llmByRound = new Map();
617
+ for (const llm of llmRows) {
618
+ const arr = llmByRound.get(llm.roundIndex) ?? [];
619
+ arr.push(llm);
620
+ llmByRound.set(llm.roundIndex, arr);
621
+ }
622
+ // Index the experiments rows by roundIndex; when missing (analysis/generation stage), use stepsByRound to render the card separately
623
+ const experimentByRound = new Map();
624
+ for (const r of rounds)
625
+ experimentByRound.set(r.roundIndex, r);
626
+ const stepsByRound = new Map();
627
+ for (const s of roundSteps) {
628
+ const arr = stepsByRound.get(s.roundIndex) ?? [];
629
+ arr.push(s);
630
+ stepsByRound.set(s.roundIndex, arr);
631
+ }
632
+ // Merge the roundIndex set → sort and generate cards one by one
633
+ const allIndexes = new Set([...rounds.map((r) => r.roundIndex), ...roundSteps.map((s) => s.roundIndex)]);
634
+ const sortedIndexes = Array.from(allIndexes).sort((a, b) => a - b);
635
+ // Also maintain a sortedExperiments list (containing only rounds that actually have an experiment row), used solely as the historical-data fallback.
636
+ // Prefer the new data: prompt_versions.parent_version_id / generate run_result.prompt_version_id,
637
+ // so the diff aligns with "from which prompt was the current version actually generated".
638
+ const sortedExperiments = rounds.slice().sort((a, b) => a.roundIndex - b.roundIndex);
639
+ return sortedIndexes.map((idx) => {
640
+ const experiment = experimentByRound.get(idx) ?? null;
641
+ const isDatasetBaseline = startingMode === 'from_dataset_only' && idx === 0;
642
+ const stepsForRound = stepsByRound.get(idx) ?? [];
643
+ const stepDtos = mapStepRowsToDtos(stepsForRound);
644
+ // status derivation: when steps data is present, derive from steps (the experiment row is not visible during the analysis / generation stage);
645
+ // otherwise fall back to experiment.status.
646
+ const status = isDatasetBaseline
647
+ ? deriveDatasetBaselineStatus(stepDtos, experiment?.status)
648
+ : deriveRoundStatusFromSteps(stepDtos, experiment?.status);
649
+ const llms = llmByRound.get(idx) ?? [];
650
+ const analysis = llms.find((r) => r.source === 'optimization_analysis');
651
+ const generate = llms.find((r) => r.source === 'optimization_generate');
652
+ this.logger.debug({
653
+ roundIndex: idx,
654
+ status,
655
+ hasAnalysis: !!analysis,
656
+ hasGenerate: !!generate,
657
+ hasExperiment: !!experiment,
658
+ llmRowsForRoundLen: llms.length,
659
+ stepsForRoundLen: stepDtos.length,
660
+ analysisStatus: analysis?.status,
661
+ generateStatus: generate?.status,
662
+ }, 'optimization_detail_round_derive');
663
+ const analysisText = analysis ? extractAnalysisSummary(analysis, idx) : undefined;
664
+ const generateText = isDatasetBaseline ? extractGenerateSummary(generate, idx) : undefined;
665
+ const errorPatterns = isDatasetBaseline ? undefined : extractErrorPatterns(analysis, idx);
666
+ const improvementSuggestions = isDatasetBaseline ? undefined : extractImprovementSuggestions(analysis, idx);
667
+ const prevVersionId = isDatasetBaseline
668
+ ? null
669
+ : resolvePromptDiffBaseVersionId(experiment, generate, sortedExperiments, baseVersionId);
670
+ const promptDiff = experiment
671
+ ? buildPromptDiff(experiment, generate, prevVersionId, promptBodyMap)
672
+ : buildPromptDiffWithoutExperiment(generate, idx, prevVersionId, promptBodyMap);
673
+ const experimentResult = experiment ? buildExperimentResult(experiment, baselineMetrics) : undefined;
674
+ const goalChips = this.buildRoundGoalChips(goals, experiment);
675
+ const { autoPatched, patchedVariables } = extractAutoPatchInfo(generate);
676
+ return {
677
+ index: idx,
678
+ status,
679
+ isBaseline: isDatasetBaseline || experiment?.isBaseline === true ? true : undefined,
680
+ kindLabel: isDatasetBaseline ? 'dataset baseline' : `Round ${idx}`,
681
+ startedAt: experiment?.startedAt?.toISOString(),
682
+ metrics: experiment ? extractRoundMetricCells(experiment.metrics) : [],
683
+ promptVersionId: experiment?.promptVersionId ?? null,
684
+ experimentId: experiment?.experimentId ?? null,
685
+ summaryFallback: generateText ?? analysisText,
686
+ errorPatterns,
687
+ improvementSuggestions,
688
+ promptDiff,
689
+ experimentResult,
690
+ steps: stepDtos,
691
+ goalChips,
692
+ autoPatched,
693
+ patchedVariables,
694
+ };
695
+ });
696
+ }
697
+ deriveGoalProgress(goals, bestMetrics) {
698
+ return goals.map((goal) => {
699
+ const current = typeof bestMetrics?.[goal.metric] === 'number' ? bestMetrics[goal.metric] : null;
700
+ const achieved = current === null
701
+ ? 'miss'
702
+ : goal.comparator === 'gte'
703
+ ? current >= goal.target
704
+ ? 'hit'
705
+ : 'miss'
706
+ : goal.comparator === 'gt'
707
+ ? current > goal.target
708
+ ? 'hit'
709
+ : 'miss'
710
+ : current <= goal.target
711
+ ? 'hit'
712
+ : 'miss';
713
+ const comparatorText = goal.comparator === 'gte' ? '≥' : goal.comparator === 'gt' ? '>' : '≤';
714
+ const percent = current === null ? 0 : Math.min(100, Math.max(0, Math.round((current / Math.max(0.0001, goal.target)) * 100)));
715
+ return {
716
+ label: this.formatMetricLabel(goal.metric),
717
+ targetText: `${comparatorText} ${goal.target}`,
718
+ currentText: current === null ? '—' : current.toFixed(3),
719
+ achieved,
720
+ percent,
721
+ };
722
+ });
723
+ }
724
+ // The "goal vs current round" chip list shown at the top-right of each round card header.
725
+ // overall scope reads metrics[goal.metric]; class scope reads from metrics.perClass by label.
726
+ buildRoundGoalChips(goals, experiment) {
727
+ if (!experiment)
728
+ return goals.map((g) => this.makeRoundGoalChip(g, null));
729
+ const metricsObj = experiment.metrics && typeof experiment.metrics === 'object'
730
+ ? experiment.metrics
731
+ : null;
732
+ if (!metricsObj)
733
+ return goals.map((g) => this.makeRoundGoalChip(g, null));
734
+ return goals.map((g) => {
735
+ let current = null;
736
+ if (g.scope === 'overall') {
737
+ current = typeof metricsObj[g.metric] === 'number' ? metricsObj[g.metric] : null;
738
+ }
739
+ else {
740
+ const perClass = Array.isArray(metricsObj['perClass']) ? metricsObj['perClass'] : [];
741
+ const entry = perClass.find((x) => !!x && typeof x === 'object' && x['label'] === g.scope);
742
+ current = entry && typeof entry[g.metric] === 'number' ? entry[g.metric] : null;
743
+ }
744
+ return this.makeRoundGoalChip(g, current);
745
+ });
746
+ }
747
+ makeRoundGoalChip(goal, current) {
748
+ const comparator = goal.comparator === 'gte' ? '≥' : goal.comparator === 'gt' ? '>' : '≤';
749
+ const achieved = current === null
750
+ ? 'miss'
751
+ : goal.comparator === 'gte'
752
+ ? current >= goal.target
753
+ ? 'hit'
754
+ : 'miss'
755
+ : goal.comparator === 'gt'
756
+ ? current > goal.target
757
+ ? 'hit'
758
+ : 'miss'
759
+ : current <= goal.target
760
+ ? 'hit'
761
+ : 'miss';
762
+ const metricLabel = this.formatMetricLabel(goal.metric);
763
+ const label = goal.scope === 'overall' ? metricLabel : `${goal.scope} ${metricLabel}`;
764
+ return {
765
+ label,
766
+ targetText: `${comparator} ${goal.target}`,
767
+ currentText: current === null ? '—' : current.toFixed(3),
768
+ achieved,
769
+ };
770
+ }
771
+ deriveEffectiveBest(row, rounds, goals, bestMetrics, sourceMetrics, startingMode) {
772
+ const persistedBest = this.derivePersistedBestCandidate(row, rounds, bestMetrics);
773
+ const baselineBest = this.deriveBaselineBestCandidate(row, rounds, sourceMetrics, startingMode);
774
+ if (!baselineBest)
775
+ return persistedBest;
776
+ if (!persistedBest)
777
+ return baselineBest;
778
+ return this.isBetterBestMetrics(baselineBest.metrics, persistedBest.metrics, goals) ? baselineBest : persistedBest;
779
+ }
780
+ derivePersistedBestCandidate(row, rounds, bestMetrics) {
781
+ if (!row.bestVersionId || !bestMetrics)
782
+ return null;
783
+ const bestRound = rounds.find((r) => r.promptVersionId === row.bestVersionId);
784
+ return {
785
+ metrics: bestMetrics,
786
+ promptVersionId: row.bestVersionId,
787
+ promptVersionNumber: row.bestVersionNumber,
788
+ generatedAtRoundLabel: row.bestVersionNumber ? `v${row.bestVersionNumber}` : '—',
789
+ generatedAtRoundIndex: bestRound?.roundIndex ?? null,
790
+ experimentId: bestRound?.experimentId ?? null,
791
+ experimentName: bestRound?.experimentName ?? null,
792
+ isBaseline: false,
793
+ };
794
+ }
795
+ deriveBaselineBestCandidate(row, rounds, sourceMetrics, startingMode) {
796
+ const baselineRound = rounds.find((r) => r.isBaseline === true || (startingMode === 'from_dataset_only' && r.roundIndex === 0));
797
+ const metrics = this.parseBestMetrics(baselineRound?.metrics ?? sourceMetrics);
798
+ if (!metrics)
799
+ return null;
800
+ return {
801
+ metrics,
802
+ promptVersionId: baselineRound?.promptVersionId ?? row.sourceExperimentPromptVersionId ?? row.baseVersionId,
803
+ promptVersionNumber: baselineRound?.promptVersionNumber ?? row.sourceExperimentPromptVersionNumber ?? row.baseVersionNumber,
804
+ generatedAtRoundLabel: 'baseline',
805
+ generatedAtRoundIndex: 0,
806
+ experimentId: baselineRound?.experimentId ?? row.sourceExperimentId,
807
+ experimentName: baselineRound?.experimentName ?? row.sourceExperimentName,
808
+ isBaseline: true,
809
+ };
810
+ }
811
+ isBetterBestMetrics(candidate, current, goals) {
812
+ if (goals.length === 0)
813
+ return false;
814
+ const candidateUnmet = this.unmetGoalCount(candidate, goals);
815
+ const currentUnmet = this.unmetGoalCount(current, goals);
816
+ if (candidateUnmet !== currentUnmet)
817
+ return candidateUnmet < currentUnmet;
818
+ return this.directionalGoalScore(candidate, goals) > this.directionalGoalScore(current, goals);
819
+ }
820
+ unmetGoalCount(metrics, goals) {
821
+ let count = 0;
822
+ for (const goal of goals) {
823
+ const value = this.readBestMetric(metrics, goal);
824
+ const hit = value !== null &&
825
+ (goal.comparator === 'gte'
826
+ ? value >= goal.target
827
+ : goal.comparator === 'gt'
828
+ ? value > goal.target
829
+ : value <= goal.target);
830
+ if (!hit)
831
+ count += 1;
832
+ }
833
+ return count;
834
+ }
835
+ directionalGoalScore(metrics, goals) {
836
+ let score = 0;
837
+ for (const goal of goals) {
838
+ const value = this.readBestMetric(metrics, goal);
839
+ score += value === null ? Number.NEGATIVE_INFINITY : goal.comparator === 'lte' ? -value : value;
840
+ }
841
+ return score;
842
+ }
843
+ readBestMetric(metrics, goal) {
844
+ const value = metrics[goal.metric];
845
+ return typeof value === 'number' && Number.isFinite(value) ? value : null;
846
+ }
847
+ deriveBestVersion(row, best) {
848
+ if (!best)
849
+ return null;
850
+ const metricCells = Object.entries(best.metrics).map(([k, v]) => ({ label: this.formatMetricLabel(k), value: v }));
851
+ return {
852
+ promptRef: row.promptName ?? '—',
853
+ promptVersion: best.promptVersionNumber ? `v${best.promptVersionNumber}` : '—',
854
+ generatedAtRoundLabel: best.generatedAtRoundLabel,
855
+ generatedAtRoundIndex: best.generatedAtRoundIndex,
856
+ metrics: metricCells,
857
+ experimentRef: best.experimentName ?? '—',
858
+ promptVersionId: best.promptVersionId,
859
+ experimentId: best.experimentId,
860
+ };
861
+ }
862
+ extractDevMockTimeline(runConfig) {
863
+ const raw = runConfig['devMockTimeline'];
864
+ if (raw === undefined || raw === null)
865
+ return null;
866
+ const parsed = shared_1.optimizationDevMockTimelineSchema.safeParse(raw);
867
+ return parsed.success ? parsed.data : null;
868
+ }
869
+ deriveGoalScope(goals) {
870
+ const classes = Array.from(new Set(goals.map((g) => g.scope).filter((scope) => scope && scope !== 'overall')));
871
+ if (classes.length === 0)
872
+ return { kind: 'overall' };
873
+ return { kind: 'class', classes };
874
+ }
875
+ deriveGoalsLines(goals) {
876
+ return goals.map((goal) => {
877
+ const tone = goal.scope === 'overall' ? 'overall' : 'class';
878
+ const label = goal.scope === 'overall'
879
+ ? this.formatMetricLabel(goal.metric)
880
+ : `${this.formatMetricLabel(goal.metric)} · ${goal.scope}`;
881
+ const comparatorText = goal.comparator === 'gte' ? '≥' : goal.comparator === 'gt' ? '>' : '≤';
882
+ const targetText = `${comparatorText} ${goal.target}`;
883
+ return { label, targetText, tone };
884
+ });
885
+ }
886
+ formatMetricLabel(metric) {
887
+ if (!metric)
888
+ return metric;
889
+ if (metric === 'fpr')
890
+ return 'FPR';
891
+ if (metric === 'f1')
892
+ return 'F1';
893
+ return metric.charAt(0).toUpperCase() + metric.slice(1);
894
+ }
895
+ deriveExperimentConfig(row, runConfig) {
896
+ if (!row.datasetName || !row.experimentModelName)
897
+ return null;
898
+ const promptVersion = row.baseVersionNumber ? `v${row.baseVersionNumber}` : '—';
899
+ const baselineExperiment = row.sourceExperimentName ?? '—';
900
+ return {
901
+ datasetName: row.datasetName,
902
+ promptName: row.promptName ?? '—',
903
+ promptVersion,
904
+ modelName: row.experimentModelName,
905
+ baselineExperiment,
906
+ temperature: typeof runConfig.temperature === 'number' ? runConfig.temperature : 0,
907
+ concurrency: typeof runConfig.concurrency === 'number' ? runConfig.concurrency : 0,
908
+ rpm: typeof runConfig.rpmLimit === 'number' ? runConfig.rpmLimit : 0,
909
+ tpm: typeof runConfig.tpmLimit === 'number' ? runConfig.tpmLimit : 0,
910
+ };
911
+ }
912
+ deriveIterationConfig(row, runConfig) {
913
+ if (!row.analysisModelName)
914
+ return null;
915
+ const noImprovement = runConfig['stopAfterNoImprovementRounds'];
916
+ const regressionRaw = runConfig['regressionThreshold'];
917
+ return {
918
+ analysisModel: row.analysisModelName,
919
+ strategy: row.strategy,
920
+ maxRounds: row.maxRounds,
921
+ noImprovementStop: typeof noImprovement === 'number' ? noImprovement : 0,
922
+ regressionThreshold: typeof regressionRaw === 'number' ? regressionRaw : 0,
923
+ };
924
+ }
925
+ deriveBaseline(row, sourceMetrics, promptBodyMap) {
926
+ if (!row.sourceExperimentName)
927
+ return null;
928
+ const promptVersionId = row.baseVersionId ?? row.sourceExperimentPromptVersionId;
929
+ const promptVersion = row.baseVersionNumber ?? row.sourceExperimentPromptVersionNumber;
930
+ const prompt = promptVersionId ? promptBodyMap.get(promptVersionId) : undefined;
931
+ const promptPreview = prompt
932
+ ? (0, shared_1.composeFullPrompt)(prompt.body ?? '', (0, shared_1.outputSchemaToJsonSchema)(prompt.outputSchema), {
933
+ language: this.parsePromptLanguage(prompt.promptLanguage),
934
+ })
935
+ : null;
936
+ const baselineExperiment = buildBaselineExperimentRow(row);
937
+ return {
938
+ promptVersion: promptVersion ? `v${promptVersion}` : '—',
939
+ baselineExperiment: row.sourceExperimentName,
940
+ metrics: extractRoundMetricCells(sourceMetrics),
941
+ promptPreview,
942
+ ...(baselineExperiment ? { experimentResult: buildExperimentResult(baselineExperiment, null) } : {}),
943
+ };
944
+ }
945
+ parseGoals(value) {
946
+ const parse = shared_1.optimizationGoalSchema.array().safeParse(value ?? []);
947
+ if (!parse.success) {
948
+ const legacy = this.parseLegacyGoals(value);
949
+ if (legacy)
950
+ return legacy;
951
+ throw new common_1.BadRequestException('optimization_goals_invalid');
952
+ }
953
+ return parse.data;
954
+ }
955
+ parseLegacyGoals(value) {
956
+ const entries = Array.isArray(value) ? value : [value];
957
+ const goals = entries.map((entry) => this.parseLegacyGoal(entry));
958
+ return goals.every((goal) => goal !== null) ? goals : null;
959
+ }
960
+ parseLegacyGoal(value) {
961
+ if (!value || typeof value !== 'object' || Array.isArray(value))
962
+ return null;
963
+ const legacy = value;
964
+ const metric = legacy['primary_metric'] ?? legacy['metric'];
965
+ const target = legacy['target'] ?? legacy['value'];
966
+ const comparator = legacy['comparator'] ?? legacy['op'] ?? 'gte';
967
+ const scope = legacy['scope'] ?? 'overall';
968
+ const parse = shared_1.optimizationGoalSchema.safeParse({ metric, comparator, target, scope });
969
+ return parse.success ? parse.data : null;
970
+ }
971
+ parseFieldWhitelist(value) {
972
+ if (value === null || value === undefined)
973
+ return null;
974
+ const parse = shared_1.optimizationFieldWhitelistSchema.safeParse(value);
975
+ return parse.success ? parse.data : null;
976
+ }
977
+ parseRunConfig(value) {
978
+ const parse = shared_1.optimizationRunConfigSchema.safeParse(value ?? {});
979
+ return parse.success ? parse.data : {};
980
+ }
981
+ parsePromptLanguage(value) {
982
+ const parse = shared_1.promptLanguageSchema.safeParse(value);
983
+ return parse.success ? parse.data : shared_1.DEFAULT_PROMPT_LANGUAGE;
984
+ }
985
+ parseBestMetrics(value) {
986
+ if (value === null || value === undefined)
987
+ return null;
988
+ if (typeof value !== 'object')
989
+ return null;
990
+ const result = {};
991
+ for (const [key, entry] of Object.entries(value)) {
992
+ if (typeof entry === 'number' && Number.isFinite(entry))
993
+ result[key] = entry;
994
+ }
995
+ return result;
996
+ }
997
+ parseSummary(value) {
998
+ if (value === null || value === undefined)
999
+ return null;
1000
+ const parse = shared_1.optimizationSummarySchema.safeParse(value);
1001
+ if (!parse.success)
1002
+ return null;
1003
+ // reason may contain upstream API payload; truncate to 500 chars to prevent leakage
1004
+ const reason = parse.data.reason.length > 500 ? `${parse.data.reason.slice(0, 500)}…` : parse.data.reason;
1005
+ return { ...parse.data, reason };
1006
+ }
1007
+ parseAnalysisFailureReason(value) {
1008
+ if (!value)
1009
+ return null;
1010
+ return value.length > 500 ? `${value.slice(0, 500)}…` : value;
1011
+ }
1012
+ filterItems(items, query) {
1013
+ const search = query.search?.trim().toLowerCase();
1014
+ return items.filter((item) => {
1015
+ if (query.status && item.status !== query.status)
1016
+ return false;
1017
+ if (!search)
1018
+ return true;
1019
+ return [
1020
+ item.name,
1021
+ item.description ?? '',
1022
+ item.datasetName,
1023
+ item.experimentModelName,
1024
+ item.analysisModelName,
1025
+ item.sourceExperimentName ?? '',
1026
+ item.promptName ?? '',
1027
+ item.createdByDisplayName ?? '',
1028
+ item.createdByUsername ?? '',
1029
+ ]
1030
+ .join(' ')
1031
+ .toLowerCase()
1032
+ .includes(search);
1033
+ });
1034
+ }
1035
+ sortItems(items, sort) {
1036
+ return [...items].sort((a, b) => {
1037
+ if (sort === 'bestMetric') {
1038
+ const av = this.bestMetricScalar(a.bestMetrics);
1039
+ const bv = this.bestMetricScalar(b.bestMetrics);
1040
+ return bv - av;
1041
+ }
1042
+ if (sort === 'round') {
1043
+ return b.currentRound / Math.max(1, b.maxRounds) - a.currentRound / Math.max(1, a.maxRounds);
1044
+ }
1045
+ return b.updatedAt.localeCompare(a.updatedAt);
1046
+ });
1047
+ }
1048
+ bestMetricScalar(metrics) {
1049
+ if (!metrics)
1050
+ return -1;
1051
+ const values = Object.values(metrics).filter((value) => Number.isFinite(value));
1052
+ return values.length === 0 ? -1 : Math.max(...values);
1053
+ }
1054
+ };
1055
+ exports.OptimizationService = OptimizationService;
1056
+ exports.OptimizationService = OptimizationService = __decorate([
1057
+ (0, common_1.Injectable)(),
1058
+ __metadata("design:paramtypes", [optimization_repository_1.OptimizationRepository,
1059
+ optimization_launcher_1.OptimizationLauncher,
1060
+ experiment_repository_1.ExperimentRepository,
1061
+ experiment_service_1.ExperimentService,
1062
+ run_result_service_1.RunResultService,
1063
+ prompt_repository_1.PromptRepository,
1064
+ access_control_service_1.AccessControlService,
1065
+ workflow_authorization_hook_1.WorkflowAuthorizationHook])
1066
+ ], OptimizationService);
1067
+ // ---------------- module helpers ----------------
1068
+ function deriveLiveProjection(row, rounds, roundSteps) {
1069
+ let currentRound = row.currentRound;
1070
+ let updatedAt = row.updatedAt;
1071
+ const baselineRoundIndexes = new Set();
1072
+ const progressRoundIndexes = [];
1073
+ for (const round of rounds) {
1074
+ if (isBaselineProgressRound(round)) {
1075
+ baselineRoundIndexes.add(round.roundIndex);
1076
+ }
1077
+ else if (isProgressRoundIndex(round.roundIndex)) {
1078
+ progressRoundIndexes.push(round.roundIndex);
1079
+ }
1080
+ updatedAt = latestDate(updatedAt, round.updatedAt, round.finishedAt, round.startedAt);
1081
+ }
1082
+ for (const step of roundSteps) {
1083
+ if (!isProgressRoundIndex(step.roundIndex)) {
1084
+ baselineRoundIndexes.add(step.roundIndex);
1085
+ }
1086
+ else {
1087
+ progressRoundIndexes.push(step.roundIndex);
1088
+ }
1089
+ updatedAt = latestDate(updatedAt, step.updatedAt, step.finishedAt, step.startedAt, step.createdAt);
1090
+ }
1091
+ if (baselineRoundIndexes.size > 0 && progressRoundIndexes.length === 0) {
1092
+ currentRound = 0;
1093
+ }
1094
+ else if (baselineRoundIndexes.has(currentRound) && !progressRoundIndexes.some((index) => index >= currentRound)) {
1095
+ currentRound = progressRoundIndexes.length > 0 ? Math.max(...progressRoundIndexes) : 0;
1096
+ }
1097
+ for (const roundIndex of progressRoundIndexes) {
1098
+ currentRound = Math.max(currentRound, roundIndex);
1099
+ }
1100
+ return {
1101
+ currentRound: Math.min(Math.max(0, row.maxRounds), Math.max(0, currentRound)),
1102
+ updatedAt,
1103
+ };
1104
+ }
1105
+ function isBaselineProgressRound(round) {
1106
+ return round.isBaseline === true || round.roundIndex <= 0;
1107
+ }
1108
+ function isProgressRoundIndex(roundIndex) {
1109
+ return roundIndex > 0;
1110
+ }
1111
+ function latestDate(current, ...candidates) {
1112
+ let latest = current;
1113
+ for (const candidate of candidates) {
1114
+ if (candidate && candidate.getTime() > latest.getTime())
1115
+ latest = candidate;
1116
+ }
1117
+ return latest;
1118
+ }
1119
+ // Map ph_runs.optimization_round_steps rows to DTO, sorted in a fixed order
1120
+ // (error_analysis → generate_prompt → experiment); the frontend stepper indexes by this order.
1121
+ const STEP_ORDER = ['error_analysis', 'generate_prompt', 'experiment'];
1122
+ function mapStepRowsToDtos(rows) {
1123
+ const byKind = new Map();
1124
+ for (const r of rows)
1125
+ byKind.set(r.step, r);
1126
+ const out = [];
1127
+ for (const kind of STEP_ORDER) {
1128
+ const row = byKind.get(kind);
1129
+ if (!row)
1130
+ continue;
1131
+ out.push({
1132
+ step: row.step,
1133
+ status: row.status,
1134
+ errorClass: row.errorClass,
1135
+ errorMessage: row.errorMessage,
1136
+ startedAt: row.startedAt?.toISOString() ?? null,
1137
+ finishedAt: row.finishedAt?.toISOString() ?? null,
1138
+ runResultId: row.runResultId,
1139
+ experimentId: row.experimentId,
1140
+ });
1141
+ }
1142
+ return out;
1143
+ }
1144
+ // Derive the round card's overall status from steps:
1145
+ // - any step running → running (highest priority, because this round is still in flight)
1146
+ // - any step failed → failed
1147
+ // - all success → success
1148
+ // - all skipped → paused
1149
+ // - none of the above (only pending) → fall back to experiment.status
1150
+ function deriveRoundStatusFromSteps(steps, experimentStatus) {
1151
+ if (steps.length === 0) {
1152
+ // When there is no round_steps data, fall back to the original logic: based on the status of the experiments row
1153
+ if (experimentStatus === 'success')
1154
+ return 'success';
1155
+ if (experimentStatus === 'failed')
1156
+ return 'failed';
1157
+ if (experimentStatus === 'stopped' || experimentStatus === 'cancelled')
1158
+ return 'paused';
1159
+ return 'running';
1160
+ }
1161
+ if (steps.some((s) => s.status === 'running'))
1162
+ return 'running';
1163
+ if (steps.some((s) => s.status === 'failed'))
1164
+ return 'failed';
1165
+ if (steps.length > 0 && steps.every((s) => s.status === 'success'))
1166
+ return 'success';
1167
+ if (steps.length > 0 && steps.every((s) => s.status === 'skipped'))
1168
+ return 'paused';
1169
+ return 'running';
1170
+ }
1171
+ function deriveDatasetBaselineStatus(steps, experimentStatus) {
1172
+ if (steps.some((s) => s.status === 'failed'))
1173
+ return 'failed';
1174
+ if (steps.some((s) => s.status === 'running'))
1175
+ return 'running';
1176
+ if (!experimentStatus)
1177
+ return 'running';
1178
+ if (experimentStatus === 'success')
1179
+ return 'success';
1180
+ if (experimentStatus === 'failed')
1181
+ return 'failed';
1182
+ if (experimentStatus === 'stopped' || experimentStatus === 'cancelled')
1183
+ return 'paused';
1184
+ return 'running';
1185
+ }
1186
+ function parsePromptLanguageValue(value) {
1187
+ const parse = shared_1.promptLanguageSchema.safeParse(value);
1188
+ return parse.success ? parse.data : shared_1.DEFAULT_PROMPT_LANGUAGE;
1189
+ }
1190
+ // If the experiment row is not yet created (analysis/generation stage), still try to reconstruct promptDiff from the generate run_result.
1191
+ // If promptVersionNumber is unavailable, fall back to a round-N label.
1192
+ function buildPromptDiffWithoutExperiment(generate, roundIndex, prevVersionId, promptBodyMap) {
1193
+ if (!generate) {
1194
+ detailHelperLogger.debug({
1195
+ roundIndex,
1196
+ status: 'no_generate',
1197
+ hasGenerate: false,
1198
+ hasNewBody: false,
1199
+ hasPrev: !!prevVersionId,
1200
+ path: 'without_experiment',
1201
+ }, 'optimization_detail_prompt_diff_resolve');
1202
+ return undefined;
1203
+ }
1204
+ const newBody = extractGeneratedPromptBody(generate);
1205
+ if (!newBody) {
1206
+ detailHelperLogger.debug({ roundIndex, status: 'no_new_body', path: 'without_experiment', generateRowStatus: generate.status }, 'optimization_detail_prompt_diff_resolve');
1207
+ return undefined;
1208
+ }
1209
+ const prev = prevVersionId ? promptBodyMap.get(prevVersionId) : undefined;
1210
+ const current = generate.promptVersionId ? promptBodyMap.get(generate.promptVersionId) : undefined;
1211
+ const effectiveNewSchema = extractGeneratedOutputSchema(generate, current?.outputSchema ?? prev?.outputSchema ?? null);
1212
+ // Bridge to standard JSON Schema before handing it to composeFullPrompt, ensuring the diff aligns with the path actually sent to the business LLM.
1213
+ const fromText = (0, shared_1.composeFullPrompt)(prev?.body ?? '', (0, shared_1.outputSchemaToJsonSchema)(prev?.outputSchema), {
1214
+ language: parsePromptLanguageValue(prev?.promptLanguage),
1215
+ });
1216
+ const toText = (0, shared_1.composeFullPrompt)(newBody, (0, shared_1.outputSchemaToJsonSchema)(effectiveNewSchema), {
1217
+ language: parsePromptLanguageValue(current?.promptLanguage ?? prev?.promptLanguage),
1218
+ });
1219
+ const fromLabel = prev?.versionNumber ? `v${prev.versionNumber}` : 'baseline';
1220
+ return {
1221
+ from: fromLabel,
1222
+ to: `Round ${roundIndex}`,
1223
+ fromText,
1224
+ toText,
1225
+ lines: [],
1226
+ };
1227
+ }
1228
+ function extractMetric(metrics, key) {
1229
+ if (!metrics || typeof metrics !== 'object')
1230
+ return null;
1231
+ const v = metrics[key];
1232
+ return typeof v === 'number' && Number.isFinite(v) ? v : null;
1233
+ }
1234
+ function extractAnalysisSummary(row, roundIndex) {
1235
+ const parsedOutputKeys = row.parsedOutput && typeof row.parsedOutput === 'object'
1236
+ ? Object.keys(row.parsedOutput).slice(0, 10)
1237
+ : [];
1238
+ const rawLen = typeof row.rawResponse === 'string' ? row.rawResponse.length : 0;
1239
+ if (row.parsedOutput && typeof row.parsedOutput === 'object') {
1240
+ const obj = row.parsedOutput;
1241
+ if (typeof obj['summary'] === 'string') {
1242
+ detailHelperLogger.debug({ roundIndex, resolvedFrom: 'summary', parsedOutputKeys, rawLen, status: row.status }, 'optimization_detail_analysis_summary_resolve');
1243
+ return obj['summary'];
1244
+ }
1245
+ if (typeof obj['errorAnalysisText'] === 'string') {
1246
+ detailHelperLogger.debug({ roundIndex, resolvedFrom: 'errorAnalysisText', parsedOutputKeys, rawLen, status: row.status }, 'optimization_detail_analysis_summary_resolve');
1247
+ return obj['errorAnalysisText'];
1248
+ }
1249
+ }
1250
+ if (typeof row.rawResponse === 'string' && row.rawResponse.length > 0) {
1251
+ detailHelperLogger.debug({ roundIndex, resolvedFrom: 'raw', parsedOutputKeys, rawLen, status: row.status }, 'optimization_detail_analysis_summary_resolve');
1252
+ return row.rawResponse.slice(0, 400);
1253
+ }
1254
+ detailHelperLogger.debug({ roundIndex, resolvedFrom: 'none', parsedOutputKeys, rawLen, status: row.status }, 'optimization_detail_analysis_summary_resolve');
1255
+ return undefined;
1256
+ }
1257
+ function extractRoundMetricCells(metrics) {
1258
+ if (!metrics || typeof metrics !== 'object')
1259
+ return [];
1260
+ const out = [];
1261
+ for (const [k, v] of Object.entries(metrics)) {
1262
+ if (typeof v === 'number' && Number.isFinite(v)) {
1263
+ out.push({ label: k, value: v });
1264
+ }
1265
+ }
1266
+ return out;
1267
+ }
1268
+ function collectPromptVersionIds(row, rounds, llmRows = []) {
1269
+ const ids = [];
1270
+ if (row.baseVersionId)
1271
+ ids.push(row.baseVersionId);
1272
+ if (row.sourceExperimentPromptVersionId)
1273
+ ids.push(row.sourceExperimentPromptVersionId);
1274
+ for (const round of rounds) {
1275
+ if (round.promptVersionId)
1276
+ ids.push(round.promptVersionId);
1277
+ if (round.parentVersionId)
1278
+ ids.push(round.parentVersionId);
1279
+ }
1280
+ for (const llm of llmRows) {
1281
+ if (llm.promptVersionId)
1282
+ ids.push(llm.promptVersionId);
1283
+ }
1284
+ return Array.from(new Set(ids));
1285
+ }
1286
+ function resolvePromptDiffBaseVersionId(experiment, generate, sortedExperiments, baseVersionId) {
1287
+ if (experiment?.parentVersionId)
1288
+ return experiment.parentVersionId;
1289
+ if (generate?.promptVersionId)
1290
+ return generate.promptVersionId;
1291
+ // Legacy fallback: when parent_version_id / generate prompt_version_id are missing,
1292
+ // fall back to the historical "previous experiment prompt" semantics.
1293
+ const prevExperimentIdx = sortedExperiments.findIndex((r) => r.roundIndex === experiment?.roundIndex);
1294
+ const prevExperiment = prevExperimentIdx > 0 ? (sortedExperiments[prevExperimentIdx - 1] ?? null) : null;
1295
+ return prevExperiment?.promptVersionId ?? baseVersionId;
1296
+ }
1297
+ function extractErrorPatterns(analysis, roundIndex) {
1298
+ if (!analysis) {
1299
+ detailHelperLogger.debug({ roundIndex, status: 'no_analysis' }, 'optimization_detail_error_patterns_resolve');
1300
+ return undefined;
1301
+ }
1302
+ const parsedOutputKeys = analysis.parsedOutput && typeof analysis.parsedOutput === 'object'
1303
+ ? Object.keys(analysis.parsedOutput).slice(0, 10)
1304
+ : [];
1305
+ if (!analysis.parsedOutput || typeof analysis.parsedOutput !== 'object') {
1306
+ detailHelperLogger.debug({ roundIndex, status: 'no_parsed_output', parsedOutputKeys, analysisRowStatus: analysis.status }, 'optimization_detail_error_patterns_resolve');
1307
+ return undefined;
1308
+ }
1309
+ const obj = analysis.parsedOutput;
1310
+ const raw = obj['errorPatterns'];
1311
+ if (!Array.isArray(raw)) {
1312
+ detailHelperLogger.debug({ roundIndex, status: 'no_array', parsedOutputKeys }, 'optimization_detail_error_patterns_resolve');
1313
+ return undefined;
1314
+ }
1315
+ if (raw.length === 0) {
1316
+ detailHelperLogger.debug({ roundIndex, status: 'empty_array', parsedOutputKeys, rawLen: 0 }, 'optimization_detail_error_patterns_resolve');
1317
+ return undefined;
1318
+ }
1319
+ const parsed = [];
1320
+ let filteredCount = 0;
1321
+ for (const item of raw) {
1322
+ if (!item || typeof item !== 'object') {
1323
+ filteredCount += 1;
1324
+ continue;
1325
+ }
1326
+ const i = item;
1327
+ const label = typeof i['label'] === 'string' ? i['label'] : null;
1328
+ if (!label) {
1329
+ filteredCount += 1;
1330
+ continue;
1331
+ }
1332
+ const count = typeof i['count'] === 'number' && Number.isFinite(i['count']) ? i['count'] : 0;
1333
+ const reason = typeof i['reason'] === 'string' ? i['reason'] : '';
1334
+ parsed.push({ label, count, reason });
1335
+ }
1336
+ if (parsed.length === 0) {
1337
+ detailHelperLogger.debug({ roundIndex, status: 'all_filtered', parsedOutputKeys, rawLen: raw.length, filteredCount }, 'optimization_detail_error_patterns_resolve');
1338
+ return undefined;
1339
+ }
1340
+ detailHelperLogger.debug({
1341
+ roundIndex,
1342
+ status: 'ok',
1343
+ parsedOutputKeys,
1344
+ rawLen: raw.length,
1345
+ keptCount: parsed.length,
1346
+ filteredCount,
1347
+ }, 'optimization_detail_error_patterns_resolve');
1348
+ const total = parsed.reduce((acc, p) => acc + p.count, 0);
1349
+ return parsed.map((p) => ({
1350
+ percent: total > 0 ? Math.round((p.count / total) * 100) : 0,
1351
+ title: p.label,
1352
+ detail: p.reason,
1353
+ count: { hit: p.count, total },
1354
+ }));
1355
+ }
1356
+ function extractImprovementSuggestions(analysis, roundIndex) {
1357
+ if (!analysis) {
1358
+ detailHelperLogger.debug({ roundIndex, status: 'no_analysis' }, 'optimization_detail_improvement_suggestions_resolve');
1359
+ return undefined;
1360
+ }
1361
+ const parsedOutputKeys = analysis.parsedOutput && typeof analysis.parsedOutput === 'object'
1362
+ ? Object.keys(analysis.parsedOutput).slice(0, 10)
1363
+ : [];
1364
+ if (!analysis.parsedOutput || typeof analysis.parsedOutput !== 'object') {
1365
+ detailHelperLogger.debug({ roundIndex, status: 'no_parsed_output', parsedOutputKeys, analysisRowStatus: analysis.status }, 'optimization_detail_improvement_suggestions_resolve');
1366
+ return undefined;
1367
+ }
1368
+ const obj = analysis.parsedOutput;
1369
+ const raw = obj['suggestedChanges'];
1370
+ if (!Array.isArray(raw)) {
1371
+ detailHelperLogger.debug({ roundIndex, status: 'no_array', parsedOutputKeys }, 'optimization_detail_improvement_suggestions_resolve');
1372
+ return undefined;
1373
+ }
1374
+ if (raw.length === 0) {
1375
+ detailHelperLogger.debug({ roundIndex, status: 'empty_array', parsedOutputKeys }, 'optimization_detail_improvement_suggestions_resolve');
1376
+ return undefined;
1377
+ }
1378
+ const out = [];
1379
+ let missingSection = 0;
1380
+ let missingChange = 0;
1381
+ let nonObject = 0;
1382
+ for (const item of raw) {
1383
+ if (!item || typeof item !== 'object') {
1384
+ nonObject += 1;
1385
+ continue;
1386
+ }
1387
+ const i = item;
1388
+ const section = typeof i['section'] === 'string' ? i['section'] : null;
1389
+ const change = typeof i['change'] === 'string' ? i['change'] : null;
1390
+ if (!section)
1391
+ missingSection += 1;
1392
+ if (!change)
1393
+ missingChange += 1;
1394
+ if (!section || !change)
1395
+ continue;
1396
+ const rationale = typeof i['rationale'] === 'string' ? i['rationale'] : '';
1397
+ const rawPriority = i['priority'];
1398
+ const priority = rawPriority === 'high' || rawPriority === 'medium' || rawPriority === 'low' ? rawPriority : undefined;
1399
+ out.push({
1400
+ section,
1401
+ title: change,
1402
+ detail: rationale || undefined,
1403
+ priority,
1404
+ });
1405
+ }
1406
+ if (out.length === 0) {
1407
+ detailHelperLogger.debug({
1408
+ roundIndex,
1409
+ status: 'all_filtered',
1410
+ parsedOutputKeys,
1411
+ rawLen: raw.length,
1412
+ missingSection,
1413
+ missingChange,
1414
+ nonObject,
1415
+ }, 'optimization_detail_improvement_suggestions_resolve');
1416
+ return undefined;
1417
+ }
1418
+ detailHelperLogger.debug({
1419
+ roundIndex,
1420
+ status: 'ok',
1421
+ parsedOutputKeys,
1422
+ rawLen: raw.length,
1423
+ keptCount: out.length,
1424
+ missingSection,
1425
+ missingChange,
1426
+ nonObject,
1427
+ }, 'optimization_detail_improvement_suggestions_resolve');
1428
+ return out;
1429
+ }
1430
+ function buildPromptDiff(round, generate, prevVersionId, promptBodyMap) {
1431
+ const roundIndex = round.roundIndex;
1432
+ if (!generate) {
1433
+ detailHelperLogger.debug({ roundIndex, status: 'no_generate', hasGenerate: false, hasNewBody: false, hasPrev: !!prevVersionId }, 'optimization_detail_prompt_diff_resolve');
1434
+ return undefined;
1435
+ }
1436
+ const generateParsedKeys = generate.parsedOutput && typeof generate.parsedOutput === 'object'
1437
+ ? Object.keys(generate.parsedOutput).slice(0, 10)
1438
+ : [];
1439
+ const newBody = extractGeneratedPromptBody(generate);
1440
+ if (!newBody) {
1441
+ detailHelperLogger.debug({
1442
+ roundIndex,
1443
+ status: 'no_new_body',
1444
+ hasGenerate: true,
1445
+ hasNewBody: false,
1446
+ hasPrev: !!prevVersionId,
1447
+ generateRowStatus: generate.status,
1448
+ generateParsedKeys,
1449
+ }, 'optimization_detail_prompt_diff_resolve');
1450
+ return undefined;
1451
+ }
1452
+ const prev = prevVersionId ? promptBodyMap.get(prevVersionId) : undefined;
1453
+ const current = round.promptVersionId ? promptBodyMap.get(round.promptVersionId) : undefined;
1454
+ const effectiveNewSchema = extractGeneratedOutputSchema(generate, current?.outputSchema ?? prev?.outputSchema ?? null);
1455
+ // Bridge to standard JSON Schema before handing it to composeFullPrompt, ensuring the diff aligns with the path actually sent to the business LLM.
1456
+ const fromText = (0, shared_1.composeFullPrompt)(prev?.body ?? '', (0, shared_1.outputSchemaToJsonSchema)(prev?.outputSchema), {
1457
+ language: parsePromptLanguageValue(prev?.promptLanguage),
1458
+ });
1459
+ const toText = (0, shared_1.composeFullPrompt)(newBody, (0, shared_1.outputSchemaToJsonSchema)(effectiveNewSchema), {
1460
+ language: parsePromptLanguageValue(current?.promptLanguage ?? prev?.promptLanguage),
1461
+ });
1462
+ const toLabel = round.promptVersionNumber ? `v${round.promptVersionNumber}` : `Round ${round.roundIndex}`;
1463
+ const fromLabel = prev?.versionNumber ? `v${prev.versionNumber}` : 'baseline';
1464
+ detailHelperLogger.debug({
1465
+ roundIndex,
1466
+ status: prev ? 'ok' : 'no_prev_version',
1467
+ hasGenerate: true,
1468
+ hasNewBody: true,
1469
+ hasPrev: !!prev,
1470
+ generateRowStatus: generate.status,
1471
+ generateParsedKeys,
1472
+ prevVersionId,
1473
+ newBodyLen: newBody.length,
1474
+ fromTextLen: fromText.length,
1475
+ schemaChanged: effectiveNewSchema !== (prev?.outputSchema ?? null),
1476
+ }, 'optimization_detail_prompt_diff_resolve');
1477
+ return {
1478
+ from: fromLabel,
1479
+ to: toLabel,
1480
+ fromText,
1481
+ toText,
1482
+ lines: [],
1483
+ };
1484
+ }
1485
+ function extractGeneratedPromptBody(generate) {
1486
+ if (!generate || !generate.parsedOutput || typeof generate.parsedOutput !== 'object')
1487
+ return null;
1488
+ const obj = generate.parsedOutput;
1489
+ const body = obj['newPromptBody'];
1490
+ if (typeof body === 'string' && body.length > 0)
1491
+ return body;
1492
+ return null;
1493
+ }
1494
+ function extractGenerateSummary(generate, roundIndex) {
1495
+ if (!generate || !generate.parsedOutput || typeof generate.parsedOutput !== 'object') {
1496
+ detailHelperLogger.debug({ roundIndex, status: generate ? 'no_parsed_output' : 'no_generate' }, 'optimization_detail_generate_summary_resolve');
1497
+ return undefined;
1498
+ }
1499
+ const obj = generate.parsedOutput;
1500
+ const summary = obj['changeSummary'];
1501
+ if (typeof summary === 'string' && summary.trim().length > 0) {
1502
+ return summary.trim().slice(0, 400);
1503
+ }
1504
+ return undefined;
1505
+ }
1506
+ // SPEC 25 §11: when generate retries are exhausted and placeholders are still missing, parsedOutput records autoPatched=true + a patchedVariables array.
1507
+ // The frontend round card renders a "system patch" chip based on this, alerting the user to manually tweak the placeholder embedding. autoPatched=false / missing field → undefined,
1508
+ // keeping the DTO field optional so legacy rounds are not polluted.
1509
+ function extractAutoPatchInfo(generate) {
1510
+ if (!generate || !generate.parsedOutput || typeof generate.parsedOutput !== 'object') {
1511
+ return {};
1512
+ }
1513
+ const obj = generate.parsedOutput;
1514
+ if (obj['autoPatched'] !== true)
1515
+ return {};
1516
+ const vars = Array.isArray(obj['patchedVariables'])
1517
+ ? obj['patchedVariables'].filter((v) => typeof v === 'string')
1518
+ : [];
1519
+ return { autoPatched: true, patchedVariables: vars };
1520
+ }
1521
+ // Extract the outputSchema actually in effect for this round from the generate run_result:
1522
+ // prefer parsedOutput.newOutputSchema (written when the LLM provided one and it passed validation); otherwise fall back (baseline / previous version schema).
1523
+ // safeValidateNewOutputSchema has already validated this on write; here we only check the "object shape" without re-validating,
1524
+ // to avoid having historical rounds' display behavior affected by new validation rules.
1525
+ function extractGeneratedOutputSchema(generate, fallbackOldSchema) {
1526
+ if (!generate || !generate.parsedOutput || typeof generate.parsedOutput !== 'object') {
1527
+ return fallbackOldSchema;
1528
+ }
1529
+ const obj = generate.parsedOutput;
1530
+ const candidate = obj['newOutputSchema'];
1531
+ if (candidate && typeof candidate === 'object' && !Array.isArray(candidate)) {
1532
+ return candidate;
1533
+ }
1534
+ return fallbackOldSchema;
1535
+ }
1536
+ function buildBaselineExperimentRow(row) {
1537
+ if (!row.sourceExperimentId || !row.sourceExperimentName || !row.sourceExperimentStatus)
1538
+ return null;
1539
+ return {
1540
+ experimentId: row.sourceExperimentId,
1541
+ experimentName: row.sourceExperimentName,
1542
+ roundIndex: 0,
1543
+ isBaseline: true,
1544
+ promptVersionId: row.sourceExperimentPromptVersionId ?? row.baseVersionId ?? '',
1545
+ promptVersionNumber: row.sourceExperimentPromptVersionNumber ?? row.baseVersionNumber,
1546
+ parentVersionId: null,
1547
+ status: row.sourceExperimentStatus,
1548
+ metrics: row.sourceExperimentMetrics,
1549
+ failureReason: row.sourceExperimentFailureReason,
1550
+ startedAt: row.sourceExperimentStartedAt,
1551
+ finishedAt: row.sourceExperimentFinishedAt,
1552
+ totalSamples: Math.max(0, row.sourceExperimentTotalSamples ?? row.datasetSamples),
1553
+ processedSamples: Math.max(0, row.sourceExperimentProcessedSamples ?? 0),
1554
+ failedSamples: Math.max(0, row.sourceExperimentFailedSamples ?? 0),
1555
+ };
1556
+ }
1557
+ function buildExperimentResult(round, baselineMetrics) {
1558
+ if (!round.experimentId)
1559
+ return undefined;
1560
+ const status = round.status === 'success' ? 'success' : round.status === 'failed' ? 'failed' : 'running';
1561
+ const samplesDone = Math.max(0, round.processedSamples);
1562
+ const samplesTotal = Math.max(samplesDone, round.totalSamples);
1563
+ const metricsObj = round.metrics && typeof round.metrics === 'object' ? round.metrics : null;
1564
+ const accuracy = metricsObj && typeof metricsObj['accuracy'] === 'number' ? metricsObj['accuracy'] : null;
1565
+ const correct = accuracy !== null ? Math.round(accuracy * samplesDone) : 0;
1566
+ const wrong = Math.max(0, samplesDone - correct);
1567
+ const elapsed = formatElapsed(round.startedAt, round.finishedAt);
1568
+ const inputTokens = metricsObj && typeof metricsObj['inputTokens'] === 'number' ? metricsObj['inputTokens'] : null;
1569
+ const outputTokens = metricsObj && typeof metricsObj['outputTokens'] === 'number' ? metricsObj['outputTokens'] : null;
1570
+ const costEstimate = metricsObj && typeof metricsObj['costEstimate'] === 'number' ? metricsObj['costEstimate'] : null;
1571
+ const tokenSummary = inputTokens !== null && outputTokens !== null
1572
+ ? `${formatThousands(inputTokens)} → ${formatThousands(outputTokens)} tok`
1573
+ : '—';
1574
+ const costLabel = costEstimate !== null ? `$${costEstimate.toFixed(4)}` : '—';
1575
+ const vsLabel = 'baseline';
1576
+ const overallRow = buildOverallRow(metricsObj, baselineMetrics, vsLabel);
1577
+ const classRows = buildClassRows(metricsObj, baselineMetrics, vsLabel);
1578
+ return {
1579
+ experimentRef: round.experimentName || `Round ${round.roundIndex}`,
1580
+ experimentStatus: status,
1581
+ samplesDone,
1582
+ samplesTotal,
1583
+ correct,
1584
+ wrong,
1585
+ elapsed,
1586
+ tokenSummary,
1587
+ costLabel,
1588
+ overallRow,
1589
+ classRows,
1590
+ vsPrevLabel: vsLabel,
1591
+ };
1592
+ }
1593
+ function buildOverallRow(metricsObj, baselineMetricsObj, vsLabel) {
1594
+ if (!metricsObj)
1595
+ return null;
1596
+ const accuracy = typeof metricsObj['accuracy'] === 'number' ? metricsObj['accuracy'] : null;
1597
+ const precision = typeof metricsObj['precision'] === 'number' ? metricsObj['precision'] : null;
1598
+ const recall = typeof metricsObj['recall'] === 'number' ? metricsObj['recall'] : null;
1599
+ if (accuracy === null && precision === null && recall === null)
1600
+ return null;
1601
+ const baselineAccuracy = baselineMetricsObj && typeof baselineMetricsObj['accuracy'] === 'number'
1602
+ ? baselineMetricsObj['accuracy']
1603
+ : null;
1604
+ const baselinePrecision = baselineMetricsObj && typeof baselineMetricsObj['precision'] === 'number'
1605
+ ? baselineMetricsObj['precision']
1606
+ : null;
1607
+ const baselineRecall = baselineMetricsObj && typeof baselineMetricsObj['recall'] === 'number'
1608
+ ? baselineMetricsObj['recall']
1609
+ : null;
1610
+ const accuracyComparison = buildMetricComparison(accuracy, baselineAccuracy, vsLabel);
1611
+ const precisionComparison = buildMetricComparison(precision, baselinePrecision, vsLabel);
1612
+ const recallComparison = buildMetricComparison(recall, baselineRecall, vsLabel);
1613
+ const vsDelta = accuracyComparison?.value ?? null;
1614
+ const vsTone = accuracyComparison?.tone ?? 'neutral';
1615
+ const deltas = compactMetricComparisons({
1616
+ accuracy: accuracyComparison,
1617
+ precision: precisionComparison,
1618
+ recall: recallComparison,
1619
+ });
1620
+ return {
1621
+ accuracy: accuracy ?? 0,
1622
+ precision: precision ?? 0,
1623
+ recall: recall ?? 0,
1624
+ vsLabel,
1625
+ vsDelta,
1626
+ vsTone,
1627
+ ...(deltas ? { deltas } : {}),
1628
+ };
1629
+ }
1630
+ function buildClassRows(metricsObj, baselineMetricsObj, vsLabel) {
1631
+ if (!metricsObj)
1632
+ return [];
1633
+ const perClass = metricsObj['perClass'];
1634
+ if (!Array.isArray(perClass))
1635
+ return [];
1636
+ const baselinePerClass = baselineMetricsObj && Array.isArray(baselineMetricsObj['perClass']) ? baselineMetricsObj['perClass'] : null;
1637
+ const baselineByLabel = new Map();
1638
+ if (baselinePerClass) {
1639
+ for (const item of baselinePerClass) {
1640
+ if (!item || typeof item !== 'object')
1641
+ continue;
1642
+ const it = item;
1643
+ const label = typeof it['label'] === 'string' ? it['label'] : null;
1644
+ if (!label)
1645
+ continue;
1646
+ baselineByLabel.set(label, {
1647
+ precision: typeof it['precision'] === 'number' ? it['precision'] : null,
1648
+ recall: typeof it['recall'] === 'number' ? it['recall'] : null,
1649
+ });
1650
+ }
1651
+ }
1652
+ const out = [];
1653
+ for (const item of perClass) {
1654
+ if (!item || typeof item !== 'object')
1655
+ continue;
1656
+ const it = item;
1657
+ const label = typeof it['label'] === 'string' ? it['label'] : null;
1658
+ if (!label)
1659
+ continue;
1660
+ const accuracy = typeof it['accuracy'] === 'number' ? it['accuracy'] : null;
1661
+ const precision = typeof it['precision'] === 'number' ? it['precision'] : 0;
1662
+ const recall = typeof it['recall'] === 'number' ? it['recall'] : 0;
1663
+ const f1 = typeof it['f1'] === 'number' ? it['f1'] : null;
1664
+ const fpr = typeof it['fpr'] === 'number' ? it['fpr'] : null;
1665
+ const baselineEntry = baselineByLabel.get(label);
1666
+ const baselinePrecision = baselineEntry?.precision ?? null;
1667
+ const baselineRecall = baselineEntry?.recall ?? null;
1668
+ const precisionComparison = buildMetricComparison(precision, baselinePrecision, vsLabel);
1669
+ const recallComparison = buildMetricComparison(recall, baselineRecall, vsLabel);
1670
+ const vsDelta = precisionComparison?.value ?? null;
1671
+ const vsTone = precisionComparison?.tone ?? 'neutral';
1672
+ const deltas = compactMetricComparisons({
1673
+ precision: precisionComparison,
1674
+ recall: recallComparison,
1675
+ });
1676
+ out.push({
1677
+ label,
1678
+ ...(accuracy !== null ? { accuracy } : {}),
1679
+ precision,
1680
+ recall,
1681
+ ...(f1 !== null ? { f1 } : {}),
1682
+ ...(fpr !== null ? { fpr } : {}),
1683
+ vsLabel,
1684
+ vsDelta,
1685
+ vsTone,
1686
+ ...(deltas ? { deltas } : {}),
1687
+ });
1688
+ }
1689
+ return out;
1690
+ }
1691
+ function buildMetricComparison(current, baseline, vsLabel, betterIsLower = false) {
1692
+ if (current === null || baseline === null)
1693
+ return undefined;
1694
+ const value = current - baseline;
1695
+ const adjusted = betterIsLower ? -value : value;
1696
+ const tone = adjusted > 0.001 ? 'ok' : adjusted < -0.001 ? 'bad' : 'neutral';
1697
+ return { value, vsLabel, tone, ...(betterIsLower ? { betterIsLower } : {}) };
1698
+ }
1699
+ function compactMetricComparisons(comparisons) {
1700
+ const entries = Object.entries(comparisons).filter((entry) => entry[1] !== undefined);
1701
+ if (entries.length === 0)
1702
+ return undefined;
1703
+ return Object.fromEntries(entries);
1704
+ }
1705
+ function formatElapsed(startedAt, finishedAt) {
1706
+ if (!startedAt)
1707
+ return '—';
1708
+ const end = finishedAt ?? new Date();
1709
+ const ms = Math.max(0, end.getTime() - startedAt.getTime());
1710
+ const sec = Math.floor(ms / 1000);
1711
+ if (sec < 60)
1712
+ return `${sec}s`;
1713
+ const min = Math.floor(sec / 60);
1714
+ const remSec = sec % 60;
1715
+ if (min < 60)
1716
+ return remSec > 0 ? `${min}m${remSec}s` : `${min}m`;
1717
+ const hr = Math.floor(min / 60);
1718
+ const remMin = min % 60;
1719
+ return remMin > 0 ? `${hr}h${remMin}m` : `${hr}h`;
1720
+ }
1721
+ function formatThousands(n) {
1722
+ return n.toLocaleString('en-US');
1723
+ }
1724
+ function normalizeOptimizationHint(value) {
1725
+ const trimmed = value?.trim();
1726
+ return trimmed && trimmed.length > 0 ? trimmed : null;
1727
+ }
1728
+ // SPEC 25 §2.1: naming rule for the prompt auto-created in from_dataset_only.
1729
+ // `<localized-prefix>-${datasetName}-${YYYY-MM-DDTHH:MM}`; the localized prefix (e.g. the Chinese for "Optimization") is hardcoded inside this function
1730
+ function buildOptimizationPromptName(datasetName, now, promptLanguage) {
1731
+ // toISOString outputs `YYYY-MM-DDTHH:MM:SS.sssZ`; truncate at the minute
1732
+ const iso = now.toISOString().slice(0, 16);
1733
+ if (promptLanguage === 'en-US')
1734
+ return `Optimization-${datasetName}-${iso}`;
1735
+ return `优化-${datasetName}-${iso}`;
1736
+ }
1737
+ // Suffix retry with an 8-char hash on prompts_project_name_unique collision — only possible under heavy concurrent creation in the same second
1738
+ // for a secondary collision; 8-char base36 hash gives 36^8 ≈ 2.8e12 of space, which is sufficient.
1739
+ function shortHash(value) {
1740
+ let h = 0;
1741
+ for (let i = 0; i < value.length; i++) {
1742
+ h = (h * 31 + value.charCodeAt(i)) >>> 0;
1743
+ }
1744
+ return h.toString(36).padStart(8, '0').slice(0, 8);
1745
+ }
1746
+ // pg unique_violation error detection: Drizzle / pg packages expose code='23505' and the message contains the constraint name.
1747
+ function isPromptNameUniqueViolation(err) {
1748
+ return (0, db_error_1.isUniqueViolation)(err, /idx_prompts_project_name_active|prompts_project_name_unique/);
1749
+ }
1750
+ function isOptimizationNameUniqueViolation(err) {
1751
+ return (0, db_error_1.isUniqueViolation)(err, /idx_optimization_project_name_active/);
1752
+ }
1753
+ //# sourceMappingURL=optimization.service.js.map