@goodandready/dsh-moa 0.2.63 → 0.2.64
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.
- package/lib/events.js +30 -0
- package/lib/index.js +23 -0
- package/lib/moa-candidates.js +3 -0
- package/lib/moa-service.js +155 -0
- package/lib/moa-stream.js +2 -1
- package/lib/pipeline/context.js +4 -2
- package/lib/pipeline/fast-mode.js +8 -0
- package/lib/pipeline/peer-critique.js +1 -1
- package/lib/pipeline/promotion.js +18 -0
- package/lib/pipeline/runner.js +38 -2
- package/lib/routes/pipeline.js +1 -0
- package/package.json +1 -1
package/lib/events.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cordis Event Bus Definitions and Helpers for dsh-moa
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const MOA_EVENTS = {
|
|
6
|
+
RUN_START: 'moa/run-start',
|
|
7
|
+
CANDIDATE_COMPLETE: 'moa/candidate-complete',
|
|
8
|
+
CRITIQUE_COMPLETE: 'moa/critique-complete',
|
|
9
|
+
JUDGE_VERDICT: 'moa/judge-verdict',
|
|
10
|
+
RUN_FINISH: 'moa/run-finish',
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Dispatches an event onto the Cordis event bus with best-effort error isolation.
|
|
15
|
+
* If ctx or ctx.emit is not available, or an event listener throws, the pipeline is not interrupted.
|
|
16
|
+
*
|
|
17
|
+
* @param {object} [ctx] - Cordis context or mock context
|
|
18
|
+
* @param {string} eventName - One of MOA_EVENTS
|
|
19
|
+
* @param {object} payload - Event data payload
|
|
20
|
+
*/
|
|
21
|
+
export function emitEvent(ctx, eventName, payload) {
|
|
22
|
+
if (!ctx || !eventName) return
|
|
23
|
+
try {
|
|
24
|
+
if (typeof ctx.emit === 'function') {
|
|
25
|
+
ctx.emit(eventName, payload)
|
|
26
|
+
}
|
|
27
|
+
} catch (_err) {
|
|
28
|
+
/* best-effort event dispatch: listener exceptions must never abort pipeline */
|
|
29
|
+
}
|
|
30
|
+
}
|
package/lib/index.js
CHANGED
|
@@ -22,6 +22,9 @@ import { createLiveCanvasClient } from './live-canvas.js'
|
|
|
22
22
|
import { registerPluginUpdater } from './updater.js'
|
|
23
23
|
import { bestEffort } from './best-effort.js'
|
|
24
24
|
import { getMoaHistory } from './history.js'
|
|
25
|
+
import { runMoAPipeline } from './moa-runner.js'
|
|
26
|
+
import { MoaService } from './moa-service.js'
|
|
27
|
+
import { MOA_EVENTS, emitEvent } from './events.js'
|
|
25
28
|
|
|
26
29
|
export const name = '@goodandready/dsh-moa'
|
|
27
30
|
export { collectProjectContext, formatProjectContext } from './file-workspace.js'
|
|
@@ -132,6 +135,23 @@ export function apply(ctx, config) {
|
|
|
132
135
|
// required to be installed).
|
|
133
136
|
const liveCanvas = createLiveCanvasClient({ getPort: () => getWebServerPort(ctx) })
|
|
134
137
|
|
|
138
|
+
// Native Cordis service: ctx.moa
|
|
139
|
+
const moaService = new MoaService(ctx, {
|
|
140
|
+
live,
|
|
141
|
+
runMoAPipeline,
|
|
142
|
+
resolvePresetForPrompt,
|
|
143
|
+
getMoaHistory,
|
|
144
|
+
callLlm,
|
|
145
|
+
liveCanvas,
|
|
146
|
+
get webServerPort() { return getWebServerPort(ctx) },
|
|
147
|
+
get timeMachineEngine() { return service(ctx, 'timeMachineEngine') },
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
if (typeof ctx.provide === 'function') {
|
|
151
|
+
ctx.provide('moa', moaService)
|
|
152
|
+
}
|
|
153
|
+
ctx.moa = moaService
|
|
154
|
+
|
|
135
155
|
// Route: /dsh-moa/status & one-click updater
|
|
136
156
|
const registerWebSurfaces = (webCtx) => {
|
|
137
157
|
if (!webCtx) return
|
|
@@ -284,6 +304,7 @@ export function apply(ctx, config) {
|
|
|
284
304
|
liveCanvas,
|
|
285
305
|
webServerPort: getWebServerPort(ctx),
|
|
286
306
|
timeMachineEngine: service(ctx, 'timeMachineEngine') || ctx.reflect?.get?.('timeMachineEngine', false),
|
|
307
|
+
ctx,
|
|
287
308
|
}
|
|
288
309
|
|
|
289
310
|
if (event.signal) {
|
|
@@ -337,6 +358,8 @@ export function apply(ctx, config) {
|
|
|
337
358
|
}
|
|
338
359
|
|
|
339
360
|
export { runMoAPipeline } from './moa-runner.js'
|
|
361
|
+
export { MoaService } from './moa-service.js'
|
|
362
|
+
export { MOA_EVENTS, emitEvent } from './events.js'
|
|
340
363
|
export { executeTestGateForCandidates, runCandidateTestGate } from './moa-test-gate.js'
|
|
341
364
|
export {
|
|
342
365
|
COMPANION_PLUGINS,
|
package/lib/moa-candidates.js
CHANGED
|
@@ -100,6 +100,7 @@ export async function runReferencesParallel(references, messages, options = {},
|
|
|
100
100
|
|
|
101
101
|
const runOne = async () => {
|
|
102
102
|
let candidateTimer = null
|
|
103
|
+
const candidateStart = Date.now()
|
|
103
104
|
try {
|
|
104
105
|
const callPromise = callWithTransientRetry(
|
|
105
106
|
callLlm,
|
|
@@ -144,6 +145,8 @@ export async function runReferencesParallel(references, messages, options = {},
|
|
|
144
145
|
text,
|
|
145
146
|
usage: costInfo,
|
|
146
147
|
costUsd: costInfo.costUsd,
|
|
148
|
+
durationMs: Date.now() - candidateStart,
|
|
149
|
+
model: slot?.model ? (slot.provider ? (slot.provider + ":" + slot.model) : slot.model) : label,
|
|
147
150
|
ok: true,
|
|
148
151
|
}
|
|
149
152
|
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native Cordis Service: ctx.moa
|
|
3
|
+
* Allows in-process MoA consensus pipeline execution and telemetry by peer plugins.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { emitEvent, MOA_EVENTS } from './events.js'
|
|
7
|
+
|
|
8
|
+
export class MoaService {
|
|
9
|
+
/**
|
|
10
|
+
* @param {import('@deepseek-ai/cordis').Context} ctx
|
|
11
|
+
* @param {object} [deps={}]
|
|
12
|
+
*/
|
|
13
|
+
constructor(ctx, deps = {}) {
|
|
14
|
+
this.ctx = ctx
|
|
15
|
+
this.deps = deps
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get live() {
|
|
19
|
+
return typeof this.deps.live === 'function' ? this.deps.live() : (this.deps.live || {})
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get all active configured presets
|
|
24
|
+
* @returns {Array<object>}
|
|
25
|
+
*/
|
|
26
|
+
getPresets() {
|
|
27
|
+
const cfg = this.live
|
|
28
|
+
return Array.isArray(cfg?.presets) ? [...cfg.presets] : []
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get preset by ID or name
|
|
33
|
+
* @param {string} [idOrName]
|
|
34
|
+
* @returns {object|null}
|
|
35
|
+
*/
|
|
36
|
+
getPreset(idOrName) {
|
|
37
|
+
const presets = this.getPresets()
|
|
38
|
+
if (!idOrName) return presets[0] || null
|
|
39
|
+
return presets.find((p) => p.id === idOrName || p.name === idOrName) || presets[0] || null
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get historical MoA runs
|
|
44
|
+
* @param {number} [limit=20]
|
|
45
|
+
* @param {number} [offset=0]
|
|
46
|
+
* @returns {object}
|
|
47
|
+
*/
|
|
48
|
+
getHistory(limit = 20, offset = 0) {
|
|
49
|
+
const getHistoryFn = this.deps.getMoaHistory
|
|
50
|
+
if (typeof getHistoryFn === 'function') {
|
|
51
|
+
return getHistoryFn(limit, offset)
|
|
52
|
+
}
|
|
53
|
+
return { runs: [], total: 0 }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Resolve best preset for a given user prompt
|
|
58
|
+
* @param {string} prompt
|
|
59
|
+
* @returns {Promise<object>}
|
|
60
|
+
*/
|
|
61
|
+
async resolvePresetForPrompt(prompt) {
|
|
62
|
+
const resolveFn = this.deps.resolvePresetForPrompt
|
|
63
|
+
if (typeof resolveFn === 'function') {
|
|
64
|
+
return resolveFn({ prompt, live: () => this.live, callLlm: this.deps.callLlm })
|
|
65
|
+
}
|
|
66
|
+
return { preset: this.getPreset() }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Run MoA consensus pipeline in-process
|
|
71
|
+
* @param {object} options
|
|
72
|
+
* @returns {Promise<object>}
|
|
73
|
+
*/
|
|
74
|
+
async run(options = {}) {
|
|
75
|
+
const runFn = this.deps.runMoAPipeline
|
|
76
|
+
if (typeof runFn !== 'function') {
|
|
77
|
+
throw new Error('[dsh-moa] runMoAPipeline is not available in MoaService')
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const {
|
|
81
|
+
prompt,
|
|
82
|
+
userPrompt,
|
|
83
|
+
messages = [],
|
|
84
|
+
preset: customPreset,
|
|
85
|
+
presetId,
|
|
86
|
+
cwd = process.cwd(),
|
|
87
|
+
signal,
|
|
88
|
+
onProgress,
|
|
89
|
+
onStreamDelta,
|
|
90
|
+
checkpoint,
|
|
91
|
+
force,
|
|
92
|
+
testGateEnabled,
|
|
93
|
+
testCommand,
|
|
94
|
+
} = options
|
|
95
|
+
|
|
96
|
+
const effectivePrompt = prompt || userPrompt || (messages[messages.length - 1]?.content) || ''
|
|
97
|
+
if (!effectivePrompt && messages.length === 0) {
|
|
98
|
+
throw new Error('[dsh-moa] prompt or userPrompt is required to run MoA')
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let preset = customPreset
|
|
102
|
+
if (!preset) {
|
|
103
|
+
if (presetId) {
|
|
104
|
+
preset = this.getPreset(presetId)
|
|
105
|
+
} else {
|
|
106
|
+
const routed = await this.resolvePresetForPrompt(effectivePrompt)
|
|
107
|
+
preset = routed?.preset || this.getPreset()
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const cfg = this.live
|
|
112
|
+
return runFn({
|
|
113
|
+
userPrompt: effectivePrompt,
|
|
114
|
+
messages,
|
|
115
|
+
preset,
|
|
116
|
+
callLlm: options.callLlm || this.deps.callLlm,
|
|
117
|
+
cwd,
|
|
118
|
+
prices: options.prices || cfg?.prices || this.deps.prices || {},
|
|
119
|
+
onProgress,
|
|
120
|
+
onStreamDelta,
|
|
121
|
+
signal,
|
|
122
|
+
checkpoint,
|
|
123
|
+
force,
|
|
124
|
+
testGateEnabled,
|
|
125
|
+
testCommand,
|
|
126
|
+
liveCanvas: options.liveCanvas || this.deps.liveCanvas,
|
|
127
|
+
webServerPort: options.webServerPort || this.deps.webServerPort,
|
|
128
|
+
timeMachineEngine: options.timeMachineEngine || this.deps.timeMachineEngine,
|
|
129
|
+
historyFilePath: options.historyFilePath || this.deps.historyFilePath,
|
|
130
|
+
ctx: this.ctx,
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Attach listener to MoA events on the underlying Cordis Context
|
|
136
|
+
* @param {string} event
|
|
137
|
+
* @param {Function} listener
|
|
138
|
+
* @returns {Function|undefined}
|
|
139
|
+
*/
|
|
140
|
+
on(event, listener) {
|
|
141
|
+
if (typeof this.ctx?.on === 'function') {
|
|
142
|
+
return this.ctx.on(event, listener)
|
|
143
|
+
}
|
|
144
|
+
return undefined
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Emit an event on the Cordis event bus
|
|
149
|
+
* @param {string} event
|
|
150
|
+
* @param {any} payload
|
|
151
|
+
*/
|
|
152
|
+
emit(event, payload) {
|
|
153
|
+
emitEvent(this.ctx, event, payload)
|
|
154
|
+
}
|
|
155
|
+
}
|
package/lib/moa-stream.js
CHANGED
|
@@ -11,7 +11,7 @@ import { runMoAPipeline } from './moa-runner.js'
|
|
|
11
11
|
/**
|
|
12
12
|
* Streams a full MoA turn into chat with live aggregator tokens and progress feedback.
|
|
13
13
|
*/
|
|
14
|
-
export async function* streamMoATurn({ targetPreset, userPrompt, messages, callLlm, cwd, prices, historyFilePath, liveCanvas, webServerPort, timeMachineEngine, force }, options = {}) {
|
|
14
|
+
export async function* streamMoATurn({ targetPreset, userPrompt, messages, callLlm, cwd, prices, historyFilePath, liveCanvas, webServerPort, timeMachineEngine, force, ctx }, options = {}) {
|
|
15
15
|
const signal = options?.signal
|
|
16
16
|
const runId = options?.runId || null
|
|
17
17
|
if (signal?.aborted) return
|
|
@@ -58,6 +58,7 @@ export async function* streamMoATurn({ targetPreset, userPrompt, messages, callL
|
|
|
58
58
|
force,
|
|
59
59
|
signal: combinedSignal,
|
|
60
60
|
runId,
|
|
61
|
+
ctx,
|
|
61
62
|
})
|
|
62
63
|
.catch((err) => ({ error: err }))
|
|
63
64
|
.finally(() => {
|
package/lib/pipeline/context.js
CHANGED
|
@@ -14,8 +14,10 @@ export async function stageProjectContext({
|
|
|
14
14
|
startTime,
|
|
15
15
|
onProgress,
|
|
16
16
|
}) {
|
|
17
|
-
const
|
|
18
|
-
? preset.reference_models
|
|
17
|
+
const rawRefs = (Array.isArray(preset?.reference_models) && preset.reference_models.length > 0)
|
|
18
|
+
? preset.reference_models
|
|
19
|
+
: (Array.isArray(preset?.references) && preset.references.length > 0 ? preset.references : [])
|
|
20
|
+
const referenceModels = rawRefs.length > 0 ? rawRefs : [{ provider: '', model: '' }]
|
|
19
21
|
const primaryJudge = preset?.aggregator?.provider && preset?.aggregator?.model
|
|
20
22
|
? preset.aggregator : { provider: '', model: '' }
|
|
21
23
|
const fallbackJudges = Array.isArray(preset?.aggregator_fallbacks) ? preset.aggregator_fallbacks : []
|
|
@@ -2,6 +2,7 @@ import { logger } from '../logger.js'
|
|
|
2
2
|
import { promoteCandidateWorkspace, cleanMoaWorkspaces } from '../workspace/fs.js'
|
|
3
3
|
import { createPromotedPreview } from '../live-canvas.js'
|
|
4
4
|
import { recordMoaRunAsync, candidatesForHistory } from '../history.js'
|
|
5
|
+
import { emitEvent, MOA_EVENTS } from '../events.js'
|
|
5
6
|
|
|
6
7
|
export async function stageFastMode({
|
|
7
8
|
successfulRefs,
|
|
@@ -18,6 +19,7 @@ export async function stageFastMode({
|
|
|
18
19
|
startTime,
|
|
19
20
|
collectedCtx,
|
|
20
21
|
liveCanvas,
|
|
22
|
+
ctx = null,
|
|
21
23
|
}) {
|
|
22
24
|
if (signal?.aborted) {
|
|
23
25
|
await cleanMoaWorkspaces(cwd, { runId })
|
|
@@ -51,6 +53,9 @@ export async function stageFastMode({
|
|
|
51
53
|
} catch (histErr) {
|
|
52
54
|
logger.warn('[dsh-moa] Failed to record fast synthesis failure in history:', histErr?.message || histErr)
|
|
53
55
|
}
|
|
56
|
+
emitEvent(ctx, MOA_EVENTS.RUN_FINISH, {
|
|
57
|
+
runId, durationMs, totalTokens, totalCostUsd, promotedFiles: [], winnerIndex: 0, error: promoteErr?.message || String(promoteErr),
|
|
58
|
+
})
|
|
54
59
|
return {
|
|
55
60
|
ok: false,
|
|
56
61
|
payload: {
|
|
@@ -87,6 +92,9 @@ export async function stageFastMode({
|
|
|
87
92
|
logger.warn('[dsh-moa] Failed to record fast synthesis run in history:', histErr?.message || histErr)
|
|
88
93
|
}
|
|
89
94
|
|
|
95
|
+
emitEvent(ctx, MOA_EVENTS.RUN_FINISH, {
|
|
96
|
+
runId, durationMs, totalTokens, totalCostUsd, promotedFiles, winnerIndex: single.index,
|
|
97
|
+
})
|
|
90
98
|
return {
|
|
91
99
|
ok: true,
|
|
92
100
|
payload: {
|
|
@@ -7,6 +7,7 @@ import { generateMoABenchmarkReport } from '../moa-report.js'
|
|
|
7
7
|
import { parseFileScores } from '../moa-workbench.js'
|
|
8
8
|
import { recordMoaRunAsync, candidatesForHistory } from '../history.js'
|
|
9
9
|
import { slotLabel } from '../moa-prompts.js'
|
|
10
|
+
import { emitEvent, MOA_EVENTS } from '../events.js'
|
|
10
11
|
|
|
11
12
|
export async function stagePromotion({
|
|
12
13
|
synthesizedText,
|
|
@@ -31,6 +32,7 @@ export async function stagePromotion({
|
|
|
31
32
|
referenceModels,
|
|
32
33
|
historyFilePath,
|
|
33
34
|
collectedCtx,
|
|
35
|
+
ctx = null,
|
|
34
36
|
}) {
|
|
35
37
|
const winningIndex = (multiJudgeResult?.consensus && multiJudgeResult?.winningCandidateIndex)
|
|
36
38
|
? multiJudgeResult.winningCandidateIndex
|
|
@@ -89,6 +91,13 @@ export async function stagePromotion({
|
|
|
89
91
|
? parseFileScores(synthesizedText, referenceOutputs)
|
|
90
92
|
: null
|
|
91
93
|
|
|
94
|
+
emitEvent(ctx, MOA_EVENTS.JUDGE_VERDICT, {
|
|
95
|
+
runId,
|
|
96
|
+
winningIndex,
|
|
97
|
+
winningModel: winnerModel,
|
|
98
|
+
scores: fileScores || multiJudgeResult?.scores || null,
|
|
99
|
+
})
|
|
100
|
+
|
|
92
101
|
try {
|
|
93
102
|
await recordMoaRunAsync({
|
|
94
103
|
id: runId, cwd, prompt: userPrompt, preset: preset?.name || 'default', isRefinement,
|
|
@@ -101,6 +110,15 @@ export async function stagePromotion({
|
|
|
101
110
|
logger.warn('[dsh-moa] Failed to record run in history:', histErr)
|
|
102
111
|
}
|
|
103
112
|
|
|
113
|
+
emitEvent(ctx, MOA_EVENTS.RUN_FINISH, {
|
|
114
|
+
runId,
|
|
115
|
+
durationMs,
|
|
116
|
+
totalTokens: usageSummary.totalTokens,
|
|
117
|
+
totalCostUsd: usageSummary.totalCostUsd,
|
|
118
|
+
promotedFiles,
|
|
119
|
+
winnerIndex: winningIndex,
|
|
120
|
+
})
|
|
121
|
+
|
|
104
122
|
return {
|
|
105
123
|
ok: true,
|
|
106
124
|
payload: {
|
package/lib/pipeline/runner.js
CHANGED
|
@@ -11,6 +11,7 @@ import { stagePeerCritique } from './peer-critique.js'
|
|
|
11
11
|
import { stageMultiJudge } from './multi-judge.js'
|
|
12
12
|
import { stageSynthesis } from './synthesis.js'
|
|
13
13
|
import { stagePromotion } from './promotion.js'
|
|
14
|
+
import { emitEvent, MOA_EVENTS } from '../events.js'
|
|
14
15
|
|
|
15
16
|
export async function runMoAPipeline({
|
|
16
17
|
userPrompt,
|
|
@@ -22,12 +23,21 @@ export async function runMoAPipeline({
|
|
|
22
23
|
testGateEnabled = null, testCommand = null, execFn = null,
|
|
23
24
|
webServerPort = null, timeMachineEngine = null, timeMachineUrl = null,
|
|
24
25
|
force = isTestEnvironment() ? true : false, checkpoint = true, checkpointFn = null, runId: customRunId = null,
|
|
26
|
+
ctx = null,
|
|
25
27
|
}) {
|
|
26
28
|
const runId = (customRunId && String(customRunId).trim()) || crypto.randomUUID()
|
|
27
29
|
const promoteOpts = { webServerPort, timeMachineEngine, timeMachineUrl, force: Boolean(force), checkpoint, checkpointFn, signal }
|
|
28
30
|
if (signal?.aborted) return { error: new Error('Turn aborted') }
|
|
29
31
|
const startTime = Date.now()
|
|
30
32
|
|
|
33
|
+
emitEvent(ctx, MOA_EVENTS.RUN_START, {
|
|
34
|
+
runId,
|
|
35
|
+
prompt: userPrompt,
|
|
36
|
+
preset: preset?.name || preset?.id || 'default',
|
|
37
|
+
candidatesCount: Array.isArray(preset?.references) ? preset.references.length : 0,
|
|
38
|
+
timestamp: new Date(startTime).toISOString(),
|
|
39
|
+
})
|
|
40
|
+
|
|
31
41
|
// Stage 1: Context & Configuration
|
|
32
42
|
const ctxStage = await stageProjectContext({
|
|
33
43
|
userPrompt, messages, preset, cwd, prices, maxTokens: preset?.max_tokens, startTime, onProgress,
|
|
@@ -54,10 +64,30 @@ export async function runMoAPipeline({
|
|
|
54
64
|
})
|
|
55
65
|
if (!candStage.ok) {
|
|
56
66
|
if (candStage.abort) return { error: candStage.error }
|
|
67
|
+
emitEvent(ctx, MOA_EVENTS.RUN_FINISH, {
|
|
68
|
+
runId,
|
|
69
|
+
durationMs: Date.now() - startTime,
|
|
70
|
+
totalTokens: candStage.failurePayload?.usage?.totalTokens || 0,
|
|
71
|
+
totalCostUsd: candStage.failurePayload?.usage?.totalCostUsd || 0,
|
|
72
|
+
promotedFiles: [],
|
|
73
|
+
winnerIndex: 0,
|
|
74
|
+
error: candStage.failurePayload?.content,
|
|
75
|
+
})
|
|
57
76
|
return candStage.failurePayload
|
|
58
77
|
}
|
|
59
78
|
const { referenceOutputs, successfulRefs } = candStage
|
|
60
79
|
|
|
80
|
+
for (const ref of referenceOutputs) {
|
|
81
|
+
emitEvent(ctx, MOA_EVENTS.CANDIDATE_COMPLETE, {
|
|
82
|
+
runId,
|
|
83
|
+
round: 1,
|
|
84
|
+
model: ref.model || ref.label,
|
|
85
|
+
success: Boolean(ref.ok),
|
|
86
|
+
durationMs: ref.durationMs || 0,
|
|
87
|
+
tokens: ref.usage?.totalTokens || 0,
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
61
91
|
// Stage 3: Clarifying Questions Branch
|
|
62
92
|
if (ctxStage.needsQuestions) {
|
|
63
93
|
return await stageClarifyingQuestions({
|
|
@@ -75,7 +105,7 @@ export async function runMoAPipeline({
|
|
|
75
105
|
allowCandidateOverride: ctxStage.allowCandidateOverride,
|
|
76
106
|
promoteOpts, cwd, runId, signal, userPrompt, preset,
|
|
77
107
|
isRefinement: ctxStage.isRefinement, historyFilePath, startTime,
|
|
78
|
-
collectedCtx: ctxStage.collectedCtx, liveCanvas,
|
|
108
|
+
collectedCtx: ctxStage.collectedCtx, liveCanvas, ctx,
|
|
79
109
|
})
|
|
80
110
|
if (!fastRes.ok) {
|
|
81
111
|
if (fastRes.abort) return { error: fastRes.error }
|
|
@@ -102,6 +132,12 @@ export async function runMoAPipeline({
|
|
|
102
132
|
options: { testGateEnabled, testCommand, execFn },
|
|
103
133
|
})
|
|
104
134
|
if (!peerRes.ok) return peerRes.abortPayload
|
|
135
|
+
if (ctxStage.isPeerCritiqueEnabled && peerRes.ok && peerRes.critiquesCount > 0) {
|
|
136
|
+
emitEvent(ctx, MOA_EVENTS.CRITIQUE_COMPLETE, {
|
|
137
|
+
runId,
|
|
138
|
+
critiquesCount: peerRes.critiquesCount,
|
|
139
|
+
})
|
|
140
|
+
}
|
|
105
141
|
|
|
106
142
|
if (signal?.aborted) {
|
|
107
143
|
await cleanMoaWorkspaces(cwd, { runId })
|
|
@@ -153,7 +189,7 @@ export async function runMoAPipeline({
|
|
|
153
189
|
promoteOpts, cwd, runId, signal, liveCanvas, startTime, chosenJudge, aggUsage,
|
|
154
190
|
userPrompt, preset, isRefinement: ctxStage.isRefinement, isFastMode: ctxStage.isFastMode,
|
|
155
191
|
effectiveReferences: ctxStage.effectiveReferences, referenceModels: ctxStage.referenceModels,
|
|
156
|
-
historyFilePath, collectedCtx: ctxStage.collectedCtx,
|
|
192
|
+
historyFilePath, collectedCtx: ctxStage.collectedCtx, ctx,
|
|
157
193
|
})
|
|
158
194
|
if (!promRes.ok) {
|
|
159
195
|
if (promRes.abort) return { error: promRes.error }
|
package/lib/routes/pipeline.js
CHANGED