@recombine-ai/platform 0.9.1-test.1 → 0.9.1-test.3

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 (148) hide show
  1. package/build/bosun-app/frontend/client/_app/immutable/chunks/{D7a3sSg2.js → 4FZq9ouk.js} +1 -1
  2. package/build/bosun-app/frontend/client/_app/immutable/chunks/4FZq9ouk.js.br +0 -0
  3. package/build/bosun-app/frontend/client/_app/immutable/chunks/4FZq9ouk.js.gz +0 -0
  4. package/build/bosun-app/frontend/client/_app/immutable/chunks/{BP8LHuYi.js → CyqpLl7J.js} +1 -1
  5. package/build/bosun-app/frontend/client/_app/immutable/chunks/CyqpLl7J.js.br +0 -0
  6. package/build/bosun-app/frontend/client/_app/immutable/chunks/CyqpLl7J.js.gz +0 -0
  7. package/build/bosun-app/frontend/client/_app/immutable/chunks/{DdbOX2wg.js → DS8Gs9Et.js} +1 -1
  8. package/build/bosun-app/frontend/client/_app/immutable/chunks/DS8Gs9Et.js.br +0 -0
  9. package/build/bosun-app/frontend/client/_app/immutable/chunks/DS8Gs9Et.js.gz +0 -0
  10. package/build/bosun-app/frontend/client/_app/immutable/chunks/{CeiDiTKi.js → RQoHgnUz.js} +1 -1
  11. package/build/bosun-app/frontend/client/_app/immutable/chunks/RQoHgnUz.js.br +0 -0
  12. package/build/bosun-app/frontend/client/_app/immutable/chunks/{CeiDiTKi.js.gz → RQoHgnUz.js.gz} +0 -0
  13. package/build/bosun-app/frontend/client/_app/immutable/chunks/{Bgf7U1Tc.js → pgn8lPGH.js} +1 -1
  14. package/build/bosun-app/frontend/client/_app/immutable/chunks/pgn8lPGH.js.br +0 -0
  15. package/build/bosun-app/frontend/client/_app/immutable/chunks/pgn8lPGH.js.gz +0 -0
  16. package/build/bosun-app/frontend/client/_app/immutable/entry/{app.D20SOteC.js → app.BnxfWXrI.js} +2 -2
  17. package/build/bosun-app/frontend/client/_app/immutable/entry/app.BnxfWXrI.js.br +0 -0
  18. package/build/bosun-app/frontend/client/_app/immutable/entry/app.BnxfWXrI.js.gz +0 -0
  19. package/build/bosun-app/frontend/client/_app/immutable/entry/start.-9o7BwoJ.js +1 -0
  20. package/build/bosun-app/frontend/client/_app/immutable/entry/start.-9o7BwoJ.js.br +0 -0
  21. package/build/bosun-app/frontend/client/_app/immutable/entry/start.-9o7BwoJ.js.gz +0 -0
  22. package/build/bosun-app/frontend/client/_app/immutable/nodes/{0.T9WgLrTj.js → 0.gzTYpBz_.js} +1 -1
  23. package/build/bosun-app/frontend/client/_app/immutable/nodes/0.gzTYpBz_.js.br +0 -0
  24. package/build/bosun-app/frontend/client/_app/immutable/nodes/0.gzTYpBz_.js.gz +0 -0
  25. package/build/bosun-app/frontend/client/_app/immutable/nodes/{1.3cRzg0A0.js → 1.BN8lpuUS.js} +1 -1
  26. package/build/bosun-app/frontend/client/_app/immutable/nodes/1.BN8lpuUS.js.br +0 -0
  27. package/build/bosun-app/frontend/client/_app/immutable/nodes/1.BN8lpuUS.js.gz +0 -0
  28. package/build/bosun-app/frontend/client/_app/immutable/nodes/{10.28fogxMw.js → 10.Dcq7nqT7.js} +1 -1
  29. package/build/bosun-app/frontend/client/_app/immutable/nodes/10.Dcq7nqT7.js.br +0 -0
  30. package/build/bosun-app/frontend/client/_app/immutable/nodes/10.Dcq7nqT7.js.gz +0 -0
  31. package/build/bosun-app/frontend/client/_app/immutable/nodes/{17.CZ8fUbCu.js → 17.Czyb-2Fi.js} +1 -1
  32. package/build/bosun-app/frontend/client/_app/immutable/nodes/17.Czyb-2Fi.js.br +0 -0
  33. package/build/bosun-app/frontend/client/_app/immutable/nodes/17.Czyb-2Fi.js.gz +0 -0
  34. package/build/bosun-app/frontend/client/_app/immutable/nodes/{18.CLTXnmXM.js → 18.DR0DZOgU.js} +1 -1
  35. package/build/bosun-app/frontend/client/_app/immutable/nodes/18.DR0DZOgU.js.br +0 -0
  36. package/build/bosun-app/frontend/client/_app/immutable/nodes/18.DR0DZOgU.js.gz +0 -0
  37. package/build/bosun-app/frontend/client/_app/immutable/nodes/{2.BVj8fduB.js → 2.BIIegE95.js} +1 -1
  38. package/build/bosun-app/frontend/client/_app/immutable/nodes/2.BIIegE95.js.br +0 -0
  39. package/build/bosun-app/frontend/client/_app/immutable/nodes/{2.BVj8fduB.js.gz → 2.BIIegE95.js.gz} +0 -0
  40. package/build/bosun-app/frontend/client/_app/immutable/nodes/{3.D1hXqvoR.js → 3.7xwGWKCq.js} +1 -1
  41. package/build/bosun-app/frontend/client/_app/immutable/nodes/3.7xwGWKCq.js.br +0 -0
  42. package/build/bosun-app/frontend/client/_app/immutable/nodes/3.7xwGWKCq.js.gz +0 -0
  43. package/build/bosun-app/frontend/client/_app/immutable/nodes/{4.Q-efp8BE.js → 4.Bfcf410Z.js} +1 -1
  44. package/build/bosun-app/frontend/client/_app/immutable/nodes/4.Bfcf410Z.js.br +0 -0
  45. package/build/bosun-app/frontend/client/_app/immutable/nodes/4.Bfcf410Z.js.gz +0 -0
  46. package/build/bosun-app/frontend/client/_app/immutable/nodes/{5.BlBkWeY0.js → 5.DSGKP42a.js} +1 -1
  47. package/build/bosun-app/frontend/client/_app/immutable/nodes/5.DSGKP42a.js.br +0 -0
  48. package/build/bosun-app/frontend/client/_app/immutable/nodes/5.DSGKP42a.js.gz +0 -0
  49. package/build/bosun-app/frontend/client/_app/immutable/nodes/{6.CQPXvkij.js → 6.BEXpYqQW.js} +1 -1
  50. package/build/bosun-app/frontend/client/_app/immutable/nodes/6.BEXpYqQW.js.br +0 -0
  51. package/build/bosun-app/frontend/client/_app/immutable/nodes/6.BEXpYqQW.js.gz +0 -0
  52. package/build/bosun-app/frontend/client/_app/immutable/nodes/{8.Bcw_NHql.js → 8.b23ZAcd2.js} +1 -1
  53. package/build/bosun-app/frontend/client/_app/immutable/nodes/8.b23ZAcd2.js.br +0 -0
  54. package/build/bosun-app/frontend/client/_app/immutable/nodes/8.b23ZAcd2.js.gz +0 -0
  55. package/build/bosun-app/frontend/client/_app/immutable/nodes/{9.iQdTFqAn.js → 9.XMXj24Oz.js} +1 -1
  56. package/build/bosun-app/frontend/client/_app/immutable/nodes/9.XMXj24Oz.js.br +0 -0
  57. package/build/bosun-app/frontend/client/_app/immutable/nodes/9.XMXj24Oz.js.gz +0 -0
  58. package/build/bosun-app/frontend/client/_app/version.json +1 -1
  59. package/build/bosun-app/frontend/client/_app/version.json.br +0 -0
  60. package/build/bosun-app/frontend/client/_app/version.json.gz +0 -0
  61. package/build/bosun-app/frontend/handler.js +4 -4
  62. package/build/bosun-app/frontend/index.js +4 -4
  63. package/build/bosun-app/frontend/server/chunks/chunks/{internal.js-BzxoI2ap.js → internal.js-3uEm1Cu_.js} +2 -2
  64. package/build/bosun-app/frontend/server/chunks/chunks/{internal.js-BzxoI2ap.js.map → internal.js-3uEm1Cu_.js.map} +1 -1
  65. package/build/bosun-app/frontend/server/chunks/{handler-DxgO6iGt.js → handler-R2AAMK_d.js} +3 -3
  66. package/build/bosun-app/frontend/server/chunks/{handler-DxgO6iGt.js.map → handler-R2AAMK_d.js.map} +1 -1
  67. package/build/bosun-app/frontend/server/chunks/{index.js-DBoPzuHL.js → index.js-B6PA6y5X.js} +2 -2
  68. package/build/bosun-app/frontend/server/chunks/{index.js-DBoPzuHL.js.map → index.js-B6PA6y5X.js.map} +1 -1
  69. package/build/bosun-app/frontend/server/chunks/{manifest.js-BZqGcn9f.js → manifest.js-Cj-2oIll.js} +14 -14
  70. package/build/bosun-app/frontend/server/chunks/{manifest.js-BZqGcn9f.js.map → manifest.js-Cj-2oIll.js.map} +1 -1
  71. package/build/bosun-app/frontend/server/chunks/nodes/{0.js-CPauJheb.js → 0.js-CbAwoV50.js} +2 -2
  72. package/build/bosun-app/frontend/server/chunks/nodes/{0.js-CPauJheb.js.map → 0.js-CbAwoV50.js.map} +1 -1
  73. package/build/bosun-app/frontend/server/chunks/nodes/{1.js-BdQRe_JA.js → 1.js-DZOnORaH.js} +2 -2
  74. package/build/bosun-app/frontend/server/chunks/nodes/{1.js-BdQRe_JA.js.map → 1.js-DZOnORaH.js.map} +1 -1
  75. package/build/bosun-app/frontend/server/chunks/nodes/{10.js-BQ80fBM8.js → 10.js-Bp0YUwTV.js} +2 -2
  76. package/build/bosun-app/frontend/server/chunks/nodes/{10.js-BQ80fBM8.js.map → 10.js-Bp0YUwTV.js.map} +1 -1
  77. package/build/bosun-app/frontend/server/chunks/nodes/{17.js-C1ScVFoO.js → 17.js-DWWiIFyE.js} +2 -2
  78. package/build/bosun-app/frontend/server/chunks/nodes/{17.js-C1ScVFoO.js.map → 17.js-DWWiIFyE.js.map} +1 -1
  79. package/build/bosun-app/frontend/server/chunks/nodes/{18.js-BErYMv7b.js → 18.js-CZhmYe9G.js} +2 -2
  80. package/build/bosun-app/frontend/server/chunks/nodes/{18.js-BErYMv7b.js.map → 18.js-CZhmYe9G.js.map} +1 -1
  81. package/build/bosun-app/frontend/server/chunks/nodes/{2.js-yX-cUBDA.js → 2.js-De6gUBiZ.js} +2 -2
  82. package/build/bosun-app/frontend/server/chunks/nodes/{2.js-yX-cUBDA.js.map → 2.js-De6gUBiZ.js.map} +1 -1
  83. package/build/bosun-app/frontend/server/chunks/nodes/{3.js-2Vh4A63Y.js → 3.js-B7tTR5rm.js} +2 -2
  84. package/build/bosun-app/frontend/server/chunks/nodes/{3.js-2Vh4A63Y.js.map → 3.js-B7tTR5rm.js.map} +1 -1
  85. package/build/bosun-app/frontend/server/chunks/nodes/{4.js-D_hzQEmf.js → 4.js-DYuQ_t39.js} +2 -2
  86. package/build/bosun-app/frontend/server/chunks/nodes/{4.js-D_hzQEmf.js.map → 4.js-DYuQ_t39.js.map} +1 -1
  87. package/build/bosun-app/frontend/server/chunks/nodes/{5.js-Bksbf9B2.js → 5.js-BuTKb27s.js} +2 -2
  88. package/build/bosun-app/frontend/server/chunks/nodes/{5.js-Bksbf9B2.js.map → 5.js-BuTKb27s.js.map} +1 -1
  89. package/build/bosun-app/frontend/server/chunks/nodes/{6.js-CBJdIQil.js → 6.js-D1pHkiGV.js} +2 -2
  90. package/build/bosun-app/frontend/server/chunks/nodes/{6.js-CBJdIQil.js.map → 6.js-D1pHkiGV.js.map} +1 -1
  91. package/build/bosun-app/frontend/server/chunks/nodes/{8.js-BESSf_tJ.js → 8.js-DX7CP-FP.js} +2 -2
  92. package/build/bosun-app/frontend/server/chunks/nodes/{8.js-BESSf_tJ.js.map → 8.js-DX7CP-FP.js.map} +1 -1
  93. package/build/bosun-app/frontend/server/chunks/nodes/{9.js-CAwt5c8Z.js → 9.js-D-08G8Ub.js} +2 -2
  94. package/build/bosun-app/frontend/server/chunks/nodes/{9.js-CAwt5c8Z.js.map → 9.js-D-08G8Ub.js.map} +1 -1
  95. package/build/engine/index.d.ts +3 -2
  96. package/build/engine/index.d.ts.map +1 -1
  97. package/build/engine/index.js +1 -1
  98. package/build/engine/interfaces/stream.d.ts +31 -1
  99. package/build/engine/interfaces/stream.d.ts.map +1 -1
  100. package/build/engine/stream/index.d.ts +3 -1
  101. package/build/engine/stream/index.d.ts.map +1 -1
  102. package/build/engine/stream/index.js +1 -0
  103. package/build/engine/stream/jev-prediction-chooser.d.ts +11 -0
  104. package/build/engine/stream/jev-prediction-chooser.d.ts.map +1 -0
  105. package/build/engine/stream/jev-prediction-chooser.js +135 -0
  106. package/build/engine/stream/predictive-prompts.d.ts +1 -1
  107. package/build/engine/stream/predictive-prompts.d.ts.map +1 -1
  108. package/build/engine/stream/predictive-prompts.js +7 -28
  109. package/build/engine/stream/predictive-workflow.d.ts.map +1 -1
  110. package/build/engine/stream/predictive-workflow.js +199 -45
  111. package/package.json +2 -1
  112. package/build/bosun-app/frontend/client/_app/immutable/chunks/BP8LHuYi.js.br +0 -0
  113. package/build/bosun-app/frontend/client/_app/immutable/chunks/BP8LHuYi.js.gz +0 -0
  114. package/build/bosun-app/frontend/client/_app/immutable/chunks/Bgf7U1Tc.js.br +0 -0
  115. package/build/bosun-app/frontend/client/_app/immutable/chunks/Bgf7U1Tc.js.gz +0 -0
  116. package/build/bosun-app/frontend/client/_app/immutable/chunks/CeiDiTKi.js.br +0 -0
  117. package/build/bosun-app/frontend/client/_app/immutable/chunks/D7a3sSg2.js.br +0 -0
  118. package/build/bosun-app/frontend/client/_app/immutable/chunks/D7a3sSg2.js.gz +0 -0
  119. package/build/bosun-app/frontend/client/_app/immutable/chunks/DdbOX2wg.js.br +0 -0
  120. package/build/bosun-app/frontend/client/_app/immutable/chunks/DdbOX2wg.js.gz +0 -0
  121. package/build/bosun-app/frontend/client/_app/immutable/entry/app.D20SOteC.js.br +0 -0
  122. package/build/bosun-app/frontend/client/_app/immutable/entry/app.D20SOteC.js.gz +0 -0
  123. package/build/bosun-app/frontend/client/_app/immutable/entry/start.BqFNrTS7.js +0 -1
  124. package/build/bosun-app/frontend/client/_app/immutable/entry/start.BqFNrTS7.js.br +0 -0
  125. package/build/bosun-app/frontend/client/_app/immutable/entry/start.BqFNrTS7.js.gz +0 -0
  126. package/build/bosun-app/frontend/client/_app/immutable/nodes/0.T9WgLrTj.js.br +0 -0
  127. package/build/bosun-app/frontend/client/_app/immutable/nodes/0.T9WgLrTj.js.gz +0 -0
  128. package/build/bosun-app/frontend/client/_app/immutable/nodes/1.3cRzg0A0.js.br +0 -0
  129. package/build/bosun-app/frontend/client/_app/immutable/nodes/1.3cRzg0A0.js.gz +0 -0
  130. package/build/bosun-app/frontend/client/_app/immutable/nodes/10.28fogxMw.js.br +0 -0
  131. package/build/bosun-app/frontend/client/_app/immutable/nodes/10.28fogxMw.js.gz +0 -0
  132. package/build/bosun-app/frontend/client/_app/immutable/nodes/17.CZ8fUbCu.js.br +0 -0
  133. package/build/bosun-app/frontend/client/_app/immutable/nodes/17.CZ8fUbCu.js.gz +0 -0
  134. package/build/bosun-app/frontend/client/_app/immutable/nodes/18.CLTXnmXM.js.br +0 -0
  135. package/build/bosun-app/frontend/client/_app/immutable/nodes/18.CLTXnmXM.js.gz +0 -0
  136. package/build/bosun-app/frontend/client/_app/immutable/nodes/2.BVj8fduB.js.br +0 -0
  137. package/build/bosun-app/frontend/client/_app/immutable/nodes/3.D1hXqvoR.js.br +0 -0
  138. package/build/bosun-app/frontend/client/_app/immutable/nodes/3.D1hXqvoR.js.gz +0 -0
  139. package/build/bosun-app/frontend/client/_app/immutable/nodes/4.Q-efp8BE.js.br +0 -0
  140. package/build/bosun-app/frontend/client/_app/immutable/nodes/4.Q-efp8BE.js.gz +0 -0
  141. package/build/bosun-app/frontend/client/_app/immutable/nodes/5.BlBkWeY0.js.br +0 -0
  142. package/build/bosun-app/frontend/client/_app/immutable/nodes/5.BlBkWeY0.js.gz +0 -0
  143. package/build/bosun-app/frontend/client/_app/immutable/nodes/6.CQPXvkij.js.br +0 -0
  144. package/build/bosun-app/frontend/client/_app/immutable/nodes/6.CQPXvkij.js.gz +0 -0
  145. package/build/bosun-app/frontend/client/_app/immutable/nodes/8.Bcw_NHql.js.br +0 -0
  146. package/build/bosun-app/frontend/client/_app/immutable/nodes/8.Bcw_NHql.js.gz +0 -0
  147. package/build/bosun-app/frontend/client/_app/immutable/nodes/9.iQdTFqAn.js.br +0 -0
  148. package/build/bosun-app/frontend/client/_app/immutable/nodes/9.iQdTFqAn.js.gz +0 -0
@@ -9,7 +9,7 @@ export function createPredictiveWorkflow(config, engineConfig, nunjucksEnv) {
9
9
  if (!predictiveModels) {
10
10
  throw new Error('Predictive models are not configured.');
11
11
  }
12
- const { predictorModel, chooserModel } = predictiveModels;
12
+ const { predictorModel, chooser } = predictiveModels;
13
13
  if (!Number.isInteger(config.predictionCount) || config.predictionCount < 1) {
14
14
  throw new Error('predictionCount must be a positive integer.');
15
15
  }
@@ -48,8 +48,10 @@ export function createPredictiveWorkflow(config, engineConfig, nunjucksEnv) {
48
48
  state.stopCurrent?.();
49
49
  state.requestGeneration += 1;
50
50
  const generation = state.requestGeneration;
51
- const isUserTurn = messages.at(-1)?.sender === 'user';
52
- const predictionSets = isUserTurn
51
+ const lastMessage = messages.at(-1);
52
+ const isUserTurn = lastMessage?.sender === 'user';
53
+ const isSubstantiveUserTurn = isUserTurn && lastMessage.text.trim().length > 0;
54
+ const predictionSets = isSubstantiveUserTurn
53
55
  ? [state.preparedPrewarm, state.preparedNormal].filter(isPreparedSet)
54
56
  : [];
55
57
  const predictions = predictionSets.flatMap((set) => set.pairs);
@@ -57,6 +59,7 @@ export function createPredictiveWorkflow(config, engineConfig, nunjucksEnv) {
57
59
  const renderedPrompt = nunjucksEnv.renderString(template, ctx || {});
58
60
  const stringifiedConversation = stringifyMessages(messages);
59
61
  const runId = crypto.randomUUID();
62
+ const responseStartedAt = Date.now();
60
63
  const mainTrace = {
61
64
  workflowId: config.name,
62
65
  workflowRunId: runId,
@@ -69,6 +72,18 @@ export function createPredictiveWorkflow(config, engineConfig, nunjucksEnv) {
69
72
  renderedPrompt,
70
73
  stringifiedConversation,
71
74
  };
75
+ logger.info('Predictive response started', {
76
+ callId,
77
+ runId,
78
+ generation,
79
+ messageCount: messages.length,
80
+ lastMessageSender: lastMessage?.sender,
81
+ lastMessageLength: lastMessage?.text.length ?? 0,
82
+ isSubstantiveUserTurn,
83
+ prewarmPredictionCount: state.preparedPrewarm?.pairs.length ?? 0,
84
+ normalPredictionCount: state.preparedNormal?.pairs.length ?? 0,
85
+ chooserCandidateCount: predictions.length,
86
+ });
72
87
  let active = true;
73
88
  let output = '';
74
89
  let outputStarted = false;
@@ -81,28 +96,55 @@ export function createPredictiveWorkflow(config, engineConfig, nunjucksEnv) {
81
96
  throw createOperationError(error, config.model, mainTrace);
82
97
  });
83
98
  const firstProvisionalChunk = readFirstNonEmptyChunk(provisionalStream, mainTrace, config.model);
84
- const chooserResults = predictions.map((pair) => choosePrediction(pair, messages, ctx, runId, isCurrent));
99
+ const chooserResult = predictions.length
100
+ ? choosePrediction(predictions, messages, ctx, runId, isCurrent)
101
+ : new Promise(() => undefined);
85
102
  function isCurrent() {
86
103
  return active && !state.closed && state.requestGeneration === generation;
87
104
  }
88
105
  function consumePredictions() {
106
+ const consumesPrewarm = isSubstantiveUserTurn && !state.prewarmConsumed;
107
+ const consumedPrewarmCount = consumesPrewarm
108
+ ? (state.preparedPrewarm?.pairs.length ?? 0)
109
+ : 0;
110
+ let consumedNormalCount = 0;
89
111
  for (const set of predictionSets) {
90
- if (state.preparedPrewarm?.id === set.id) {
91
- state.preparedPrewarm = undefined;
92
- state.prewarmConsumed = true;
93
- }
94
112
  if (state.preparedNormal?.id === set.id) {
113
+ consumedNormalCount = set.pairs.length;
95
114
  state.preparedNormal = undefined;
96
115
  }
97
116
  }
117
+ if (consumesPrewarm) {
118
+ state.preparedPrewarm = undefined;
119
+ state.prewarmConsumed = true;
120
+ }
121
+ if (isSubstantiveUserTurn || consumedNormalCount > 0) {
122
+ logger.info('Prepared predictions consumed', {
123
+ callId,
124
+ runId,
125
+ generation,
126
+ prewarmPredictionCount: consumedPrewarmCount,
127
+ normalPredictionCount: consumedNormalCount,
128
+ });
129
+ }
98
130
  }
99
- function publish(controller, delta) {
131
+ function publish(controller, delta, source, match) {
100
132
  if (!isCurrent() || !delta)
101
133
  return false;
102
134
  if (!outputStarted) {
103
135
  outputStarted = true;
104
136
  consumePredictions();
105
137
  logger.debug(`[MARK] Predictive workflow TTFT: ${config.name}`);
138
+ logger.info('Predictive response winner selected', {
139
+ callId,
140
+ runId,
141
+ generation,
142
+ source,
143
+ ttftMs: Date.now() - responseStartedAt,
144
+ chooserCandidateCount: predictions.length,
145
+ selectedIndex: match?.index,
146
+ selectedScore: match?.score,
147
+ });
106
148
  }
107
149
  output += delta;
108
150
  controller.enqueue({ role: 'agent', delta });
@@ -116,6 +158,13 @@ export function createPredictiveWorkflow(config, engineConfig, nunjucksEnv) {
116
158
  state.stopCurrent = undefined;
117
159
  mainTrace.response = output;
118
160
  void addAndFlushTrace(stepTracer, mainTrace, logger);
161
+ logger.info('Predictive response completed', {
162
+ callId,
163
+ runId,
164
+ generation,
165
+ durationMs: Date.now() - responseStartedAt,
166
+ outputLength: output.length,
167
+ });
119
168
  if (!state.closed) {
120
169
  startNormalPrediction(messages, output, renderedPrompt, ctx, state, generation);
121
170
  }
@@ -124,6 +173,13 @@ export function createPredictiveWorkflow(config, engineConfig, nunjucksEnv) {
124
173
  if (!active)
125
174
  return;
126
175
  active = false;
176
+ logger.info('Predictive response stopped', {
177
+ callId,
178
+ runId,
179
+ generation,
180
+ outputStarted,
181
+ durationMs: Date.now() - responseStartedAt,
182
+ });
127
183
  void provisionalReader?.cancel();
128
184
  try {
129
185
  publicController?.close();
@@ -163,27 +219,27 @@ export function createPredictiveWorkflow(config, engineConfig, nunjucksEnv) {
163
219
  }
164
220
  async function orchestrate(controller) {
165
221
  try {
166
- const winner = await Promise.race([firstProvisionalChunk, ...chooserResults]);
222
+ const winner = await Promise.race([firstProvisionalChunk, chooserResult]);
167
223
  if (!isCurrent())
168
224
  return;
169
225
  if (winner.kind === 'prediction') {
170
226
  void firstProvisionalChunk
171
227
  .then(({ reader }) => reader.cancel())
172
228
  .catch(() => undefined);
173
- publish(controller, winner.pair.agentResponse);
229
+ publish(controller, winner.pair.agentResponse, winner.kind, winner);
174
230
  controller.close();
175
231
  finishSuccessfulResponse();
176
232
  return;
177
233
  }
178
234
  provisionalReader = winner.reader;
179
- if (!publish(controller, winner.chunk))
235
+ if (!publish(controller, winner.chunk, winner.kind))
180
236
  return;
181
237
  while (isCurrent()) {
182
238
  try {
183
239
  const next = await winner.reader.read();
184
240
  if (next.done)
185
241
  break;
186
- if (!publish(controller, next.value))
242
+ if (!publish(controller, next.value, winner.kind))
187
243
  return;
188
244
  }
189
245
  catch (error) {
@@ -210,14 +266,42 @@ export function createPredictiveWorkflow(config, engineConfig, nunjucksEnv) {
210
266
  });
211
267
  }
212
268
  function prewarmForUsersHello(ctx) {
213
- if (config.prewarmPredictions.length === 0)
214
- return;
215
269
  const callId = getCallId(ctx);
216
270
  const state = getOrCreateState(callId);
217
- if (state.prewarmStarted || state.prewarmConsumed)
271
+ if (state.prewarmStarted || state.prewarmConsumed) {
272
+ logger.debug('Opening prediction prewarm skipped', {
273
+ callId,
274
+ prewarmStarted: state.prewarmStarted,
275
+ prewarmConsumed: state.prewarmConsumed,
276
+ });
218
277
  return;
278
+ }
219
279
  state.prewarmStarted = true;
280
+ logger.info('Opening prediction prewarm started', {
281
+ callId,
282
+ predictionCount: config.prewarmPredictions.length,
283
+ chooserPrewarmEnabled: chooser.prewarm !== undefined,
284
+ });
285
+ const chooserPrewarmStartedAt = Date.now();
286
+ void chooser
287
+ .prewarm?.()
288
+ .then(() => {
289
+ logger.info('Prediction chooser prewarm completed', {
290
+ callId,
291
+ durationMs: Date.now() - chooserPrewarmStartedAt,
292
+ });
293
+ })
294
+ .catch((error) => {
295
+ logger.warn('Prediction chooser prewarm failed', {
296
+ callId,
297
+ durationMs: Date.now() - chooserPrewarmStartedAt,
298
+ error,
299
+ });
300
+ });
301
+ if (config.prewarmPredictions.length === 0)
302
+ return;
220
303
  const generation = state.requestGeneration;
304
+ const predictionStartedAt = Date.now();
221
305
  const task = (async () => {
222
306
  const template = typeof config.prompt === 'string' ? config.prompt : await config.prompt.content();
223
307
  const renderedPrompt = nunjucksEnv.renderString(template, ctx || {});
@@ -229,18 +313,32 @@ export function createPredictiveWorkflow(config, engineConfig, nunjucksEnv) {
229
313
  supplementalPredictions: config.prewarmPredictions,
230
314
  traceName: 'predictor[prewarm]',
231
315
  });
232
- if (!state.closed && !state.prewarmConsumed && state.requestGeneration === generation) {
316
+ const stored = !state.closed && !state.prewarmConsumed;
317
+ if (stored) {
233
318
  state.preparedPrewarm = { id: crypto.randomUUID(), pairs: prediction.pairs };
319
+ }
320
+ logger.info('Opening prediction prewarm completed', {
321
+ callId,
322
+ durationMs: Date.now() - predictionStartedAt,
323
+ predictionCount: prediction.pairs.length,
324
+ stored,
325
+ discardReason: state.closed
326
+ ? 'call-closed'
327
+ : state.prewarmConsumed
328
+ ? 'opening-turn-consumed'
329
+ : undefined,
330
+ });
331
+ if (!state.closed) {
234
332
  await addAndFlushTrace(stepTracer, prediction.trace, logger);
235
333
  }
236
334
  })();
237
- trackPrediction(task, state, ctx, generation);
335
+ trackPrediction(task, state, ctx, generation, () => !state.prewarmConsumed);
238
336
  }
239
337
  async function waitForPendingPrediction(callId) {
240
338
  const state = states.get(callId);
241
339
  if (!state)
242
340
  return;
243
- await Promise.all([...(state.pendingPredictions.get(state.requestGeneration) ?? [])]);
341
+ await Promise.all([...state.pendingPredictions.values()].flatMap((predictions) => [...predictions]));
244
342
  }
245
343
  function clear(callId) {
246
344
  const state = states.get(callId);
@@ -267,6 +365,12 @@ export function createPredictiveWorkflow(config, engineConfig, nunjucksEnv) {
267
365
  return state;
268
366
  }
269
367
  function startNormalPrediction(messages, agentResponse, renderedMainPrompt, ctx, state, generation) {
368
+ const callId = getCallId(ctx);
369
+ const predictionStartedAt = Date.now();
370
+ logger.debug('Normal prediction generation started', {
371
+ callId,
372
+ generation,
373
+ });
270
374
  const conversationWithResponse = [
271
375
  stringifyMessages(messages).trim(),
272
376
  `Agent: ${agentResponse.trim()}`,
@@ -281,16 +385,31 @@ export function createPredictiveWorkflow(config, engineConfig, nunjucksEnv) {
281
385
  supplementalPredictions: config.normalFlowPredictions,
282
386
  traceName: 'predictor[normal]',
283
387
  }).then(async (prediction) => {
284
- if (!state.closed && state.requestGeneration === generation) {
388
+ const stored = !state.closed && state.requestGeneration === generation;
389
+ if (stored) {
285
390
  state.preparedNormal = { id: crypto.randomUUID(), pairs: prediction.pairs };
391
+ }
392
+ logger.info('Normal prediction generation completed', {
393
+ callId,
394
+ generation,
395
+ durationMs: Date.now() - predictionStartedAt,
396
+ predictionCount: prediction.pairs.length,
397
+ stored,
398
+ discardReason: state.closed
399
+ ? 'call-closed'
400
+ : state.requestGeneration !== generation
401
+ ? 'conversation-changed'
402
+ : undefined,
403
+ });
404
+ if (!state.closed) {
286
405
  await addAndFlushTrace(stepTracer, prediction.trace, logger);
287
406
  }
288
407
  });
289
408
  trackPrediction(task, state, ctx, generation);
290
409
  }
291
- function trackPrediction(task, state, ctx, generation) {
410
+ function trackPrediction(task, state, ctx, generation, isRelevant = () => state.requestGeneration === generation) {
292
411
  const handled = task.catch(async (error) => {
293
- if (state.closed || state.requestGeneration !== generation)
412
+ if (state.closed || !isRelevant())
294
413
  return;
295
414
  const operation = isOperationError(error) ? error : undefined;
296
415
  const cause = operation?.cause ?? error;
@@ -364,46 +483,81 @@ export function createPredictiveWorkflow(config, engineConfig, nunjucksEnv) {
364
483
  });
365
484
  return { pairs, trace };
366
485
  }
367
- async function choosePrediction(pair, messages, ctx, parentRunId, isActive) {
486
+ async function choosePrediction(pairs, messages, ctx, parentRunId, isActive) {
487
+ const callId = getCallId(ctx);
488
+ const chooserStartedAt = Date.now();
368
489
  const actualUserResponse = messages.at(-1)?.text ?? '';
369
- const history = stringifyMessages(messages.slice(0, -1));
370
- const rendered = nunjucksEnv.renderString(chooserPrompt, {
371
- history,
372
- reference: pair.userResponse,
373
- alternative: actualUserResponse,
374
- });
490
+ const history = messages.slice(0, -1);
491
+ const stringifiedHistory = stringifyMessages(history);
492
+ const input = { history, actualUserResponse, candidates: pairs };
375
493
  const trace = {
376
494
  workflowId: config.name,
377
495
  workflowRunId: parentRunId,
378
496
  name: 'chooser',
379
- model: JSON.stringify(chooserModel.getOptions()),
497
+ model: JSON.stringify(chooser.getOptions()),
380
498
  createdAt: Date.now(),
381
499
  response: '',
382
500
  receivedContext: ctx,
383
501
  receivedPrompt: chooserPrompt,
384
- renderedPrompt: rendered,
385
- stringifiedConversation: history,
502
+ renderedPrompt: JSON.stringify(input),
503
+ stringifiedConversation: stringifiedHistory,
386
504
  };
387
- let raw;
505
+ let result;
388
506
  try {
389
- raw = await chooserModel.generateResponse('', rendered);
507
+ result = await chooser.choose(input);
390
508
  }
391
509
  catch (error) {
392
- throw createOperationError(error, chooserModel, trace);
510
+ throw createOperationError(error, chooser, trace);
393
511
  }
394
- if (!isActive())
512
+ if (!isActive()) {
513
+ logger.debug('Prediction chooser result ignored', {
514
+ callId,
515
+ parentRunId,
516
+ durationMs: Date.now() - chooserStartedAt,
517
+ reason: 'response-replaced',
518
+ });
395
519
  return new Promise(() => undefined);
396
- const label = raw.trim();
397
- trace.response = label;
398
- if (label !== 'N' && label !== 'U') {
399
- if (label === 'E') {
400
- void addAndFlushTrace(stepTracer, trace, logger);
401
- return { kind: 'prediction', pair };
402
- }
403
- throw createOperationError(new Error(`Chooser returned an invalid label: ${label}`), chooserModel, trace);
404
520
  }
521
+ trace.response = JSON.stringify(result);
522
+ if (result.scores.length !== pairs.length) {
523
+ throw createOperationError(new Error('Chooser returned the wrong number of candidate scores.'), chooser, trace);
524
+ }
525
+ if (result.selectedIndex === undefined) {
526
+ logger.info('Prediction chooser completed', {
527
+ callId,
528
+ parentRunId,
529
+ durationMs: Date.now() - chooserStartedAt,
530
+ candidateCount: pairs.length,
531
+ scores: result.scores,
532
+ usage: result.usage,
533
+ diagnostics: result.diagnostics,
534
+ });
535
+ void addAndFlushTrace(stepTracer, trace, logger);
536
+ return new Promise(() => undefined);
537
+ }
538
+ if (!Number.isInteger(result.selectedIndex) ||
539
+ result.selectedIndex < 0 ||
540
+ result.selectedIndex >= pairs.length) {
541
+ throw createOperationError(new Error('Chooser returned an invalid candidate index.'), chooser, trace);
542
+ }
543
+ logger.info('Prediction chooser completed', {
544
+ callId,
545
+ parentRunId,
546
+ durationMs: Date.now() - chooserStartedAt,
547
+ candidateCount: pairs.length,
548
+ selectedIndex: result.selectedIndex,
549
+ selectedScore: result.scores[result.selectedIndex],
550
+ scores: result.scores,
551
+ usage: result.usage,
552
+ diagnostics: result.diagnostics,
553
+ });
405
554
  void addAndFlushTrace(stepTracer, trace, logger);
406
- return new Promise(() => undefined);
555
+ return {
556
+ kind: 'prediction',
557
+ pair: pairs[result.selectedIndex],
558
+ index: result.selectedIndex,
559
+ score: result.scores[result.selectedIndex],
560
+ };
407
561
  }
408
562
  return { run, prewarmForUsersHello, waitForPendingPrediction, clear };
409
563
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@recombine-ai/platform",
3
3
  "type": "module",
4
- "version": "0.9.1-test.1",
4
+ "version": "0.9.1-test.3",
5
5
  "description": "The Recombine platform: engine, telescope, dockyard, call control, the voice agent API, and the Bosun bindings in one package",
6
6
  "license": "EULA",
7
7
  "author": "recombine.ai",
@@ -42,6 +42,7 @@
42
42
  "registry": "https://registry.npmjs.org"
43
43
  },
44
44
  "dependencies": {
45
+ "@typesafe-ai/sdk": "0.6.0",
45
46
  "dotenv": "17.4.2",
46
47
  "esbuild": "0.27.3",
47
48
  "fast-xml-parser": "5.7.3",
@@ -1 +0,0 @@
1
- import{a as r}from"../chunks/BP8LHuYi.js";import{y as t}from"../chunks/D7a3sSg2.js";export{t as load_css,r as start};