@goodandready/dsh-moa 0.2.76 → 0.2.78
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/README.md +4 -0
- package/README.ru.md +4 -0
- package/README.zh.md +4 -0
- package/lib/client.js +102 -2
- package/lib/events.js +4 -0
- package/lib/moa-schema.js +24 -0
- package/lib/pipeline/candidates.js +20 -2
- package/lib/pipeline/mutation-tester.js +323 -0
- package/lib/pipeline/privacy-sanitizer.js +213 -0
- package/lib/pipeline/runner.js +66 -6
- package/lib/pipeline/runtime-profiler.js +270 -0
- package/lib/pipeline/speculative-verifier.js +212 -0
- package/package.json +1 -1
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSeek Harness Mixture of Agents (MoA) — Heterogeneous Speculative Decoding (#317)
|
|
3
|
+
*
|
|
4
|
+
* Implements a two-phase speculative pipeline:
|
|
5
|
+
* 1. A lightweight local edge model (e.g. Qwen 2.5 Coder 3B/7B via Ollama/MiniPC) rapidly produces
|
|
6
|
+
* a speculative draft code block at high token throughput.
|
|
7
|
+
* 2. A powerful cloud verifier model (e.g. DeepSeek R1, Claude, GPT) receives the speculative draft
|
|
8
|
+
* and executes batched validation, bug fixing, and completion in a single fast verification pass.
|
|
9
|
+
*
|
|
10
|
+
* Measures effective throughput (tok/sec) and speedup acceleration vs non-speculative generation.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { MOA_EVENTS, emitEvent } from '../events.js'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Estimates token count from text using character heuristic (~4 chars per token).
|
|
17
|
+
*
|
|
18
|
+
* @param {string} text
|
|
19
|
+
* @returns {number}
|
|
20
|
+
*/
|
|
21
|
+
export function estimateTokens(text) {
|
|
22
|
+
if (!text || typeof text !== 'string') return 0
|
|
23
|
+
return Math.max(1, Math.round(text.length / 4))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Executes a heterogeneous speculative decoding turn: local draft + cloud verifier.
|
|
28
|
+
*
|
|
29
|
+
* @param {object} params
|
|
30
|
+
* @param {string} params.prompt
|
|
31
|
+
* @param {object} params.draftSlot - { provider, model }
|
|
32
|
+
* @param {object} params.targetSlot - { provider, model }
|
|
33
|
+
* @param {Function} params.callLlm - LLM execution callback
|
|
34
|
+
* @param {object} [params.options]
|
|
35
|
+
* @returns {Promise<object>} Speculative decoding result
|
|
36
|
+
*/
|
|
37
|
+
export async function runSpeculativeVerification({
|
|
38
|
+
prompt,
|
|
39
|
+
draftSlot,
|
|
40
|
+
targetSlot,
|
|
41
|
+
callLlm,
|
|
42
|
+
options = {},
|
|
43
|
+
}) {
|
|
44
|
+
const {
|
|
45
|
+
ctx,
|
|
46
|
+
runId,
|
|
47
|
+
onProgress,
|
|
48
|
+
blockSize = 256,
|
|
49
|
+
signal,
|
|
50
|
+
} = options
|
|
51
|
+
|
|
52
|
+
if (!callLlm || typeof callLlm !== 'function') {
|
|
53
|
+
throw new Error('callLlm function is required for speculative verification')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const draftLabel = `${draftSlot?.provider || 'local'}:${draftSlot?.model || 'edge-draft'}`
|
|
57
|
+
const targetLabel = `${targetSlot?.provider || 'cloud'}:${targetSlot?.model || 'verifier'}`
|
|
58
|
+
|
|
59
|
+
// Phase 1: Local Edge Draft Generation
|
|
60
|
+
let draftText = ''
|
|
61
|
+
let draftDurationMs = 0
|
|
62
|
+
const draftStart = Date.now()
|
|
63
|
+
|
|
64
|
+
if (typeof onProgress === 'function') {
|
|
65
|
+
onProgress(`⚡ *Speculative Decoding*: Generating speculative draft (${blockSize} tok) via ` + draftLabel + '...\n')
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const draftRes = await callLlm({
|
|
70
|
+
provider: draftSlot?.provider,
|
|
71
|
+
model: draftSlot?.model,
|
|
72
|
+
messages: [{ role: 'user', content: prompt }],
|
|
73
|
+
maxTokens: blockSize,
|
|
74
|
+
temperature: 0.3,
|
|
75
|
+
signal,
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
draftText = typeof draftRes === 'string' ? draftRes : draftRes?.content || draftRes?.text || ''
|
|
79
|
+
draftDurationMs = Math.max(1, Date.now() - draftStart)
|
|
80
|
+
} catch (err) {
|
|
81
|
+
// Graceful fallback: If draft model fails, target model generates directly
|
|
82
|
+
if (typeof onProgress === 'function') {
|
|
83
|
+
onProgress(`⚠️ *Speculative Draft Failed* (${err?.message || err}); falling back to direct cloud generation...\n`)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const directStart = Date.now()
|
|
87
|
+
const directRes = await callLlm({
|
|
88
|
+
provider: targetSlot?.provider,
|
|
89
|
+
model: targetSlot?.model,
|
|
90
|
+
messages: [{ role: 'user', content: prompt }],
|
|
91
|
+
signal,
|
|
92
|
+
})
|
|
93
|
+
const directDurationMs = Math.max(1, Date.now() - directStart)
|
|
94
|
+
const directText = typeof directRes === 'string' ? directRes : directRes?.content || directRes?.text || ''
|
|
95
|
+
const directTokens = estimateTokens(directText)
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
output: directText,
|
|
99
|
+
draftUsed: false,
|
|
100
|
+
draftText: null,
|
|
101
|
+
draftDurationMs: 0,
|
|
102
|
+
verifierDurationMs: directDurationMs,
|
|
103
|
+
totalDurationMs: directDurationMs,
|
|
104
|
+
effectiveTokensPerSec: Math.round((directTokens / (directDurationMs / 1000))),
|
|
105
|
+
speedupFactor: 1.0,
|
|
106
|
+
summary: 'Speculative fallback: direct generation without draft.',
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Phase 2: Cloud Verifier Validation & Completion
|
|
111
|
+
const verifierPrompt = [
|
|
112
|
+
'You are an expert Cloud Verifier performing speculative code completion.',
|
|
113
|
+
'Below is the user prompt and a fast speculative draft block from a local edge model.',
|
|
114
|
+
'Review the draft for correctness, patch any edge case errors or bugs, and output the complete finalized solution.',
|
|
115
|
+
'\n### Original User Task:\n' + prompt,
|
|
116
|
+
`\n### Speculative Edge Draft (${draftLabel}):\n` + draftText,
|
|
117
|
+
'\nProvide your verified, final solution code below:',
|
|
118
|
+
].join('\n')
|
|
119
|
+
|
|
120
|
+
if (typeof onProgress === 'function') {
|
|
121
|
+
onProgress(`🔍 *Speculative Verification*: Verifying and refining draft via ` + targetLabel + '...\n')
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const verifierStart = Date.now()
|
|
125
|
+
const verifierRes = await callLlm({
|
|
126
|
+
provider: targetSlot?.provider,
|
|
127
|
+
model: targetSlot?.model,
|
|
128
|
+
messages: [{ role: 'user', content: verifierPrompt }],
|
|
129
|
+
temperature: 0.2,
|
|
130
|
+
signal,
|
|
131
|
+
})
|
|
132
|
+
const verifierDurationMs = Math.max(1, Date.now() - verifierStart)
|
|
133
|
+
const finalOutput = typeof verifierRes === 'string' ? verifierRes : verifierRes?.content || verifierRes?.text || ''
|
|
134
|
+
const totalDurationMs = draftDurationMs + verifierDurationMs
|
|
135
|
+
|
|
136
|
+
const outputTokens = estimateTokens(finalOutput)
|
|
137
|
+
const effectiveTokensPerSec = totalDurationMs > 0 ? Math.round((outputTokens / (totalDurationMs / 1000))) : 0
|
|
138
|
+
|
|
139
|
+
// Estimated baseline: Cloud verifier model generating all tokens from scratch typically takes ~2x longer per token
|
|
140
|
+
const estimatedBaselineMs = Math.round(outputTokens * 35) // ~28-35ms per token for large cloud models
|
|
141
|
+
const speedupFactor = totalDurationMs > 0 ? Number(Math.max(1.0, (estimatedBaselineMs / totalDurationMs)).toFixed(2)) : 1.0
|
|
142
|
+
|
|
143
|
+
if (ctx) {
|
|
144
|
+
emitEvent(ctx, MOA_EVENTS.SPECULATIVE_DRAFT, {
|
|
145
|
+
runId,
|
|
146
|
+
draftModel: draftLabel,
|
|
147
|
+
verifierModel: targetLabel,
|
|
148
|
+
draftDurationMs,
|
|
149
|
+
verifierDurationMs,
|
|
150
|
+
totalDurationMs,
|
|
151
|
+
effectiveTokensPerSec,
|
|
152
|
+
speedupFactor,
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (typeof onProgress === 'function') {
|
|
157
|
+
onProgress(`🚀 *Speculative Acceleration*: ${effectiveTokensPerSec} effective tok/sec (~x${speedupFactor} speedup)\n`)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
output: finalOutput,
|
|
162
|
+
draftUsed: true,
|
|
163
|
+
draftText,
|
|
164
|
+
draftDurationMs,
|
|
165
|
+
verifierDurationMs,
|
|
166
|
+
totalDurationMs,
|
|
167
|
+
effectiveTokensPerSec,
|
|
168
|
+
speedupFactor,
|
|
169
|
+
summary: `⚡ Speculative Decoding: draft ${draftDurationMs}ms + verifier ${verifierDurationMs}ms (${effectiveTokensPerSec} tok/sec, ~x${speedupFactor} speedup)`,
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Creates a wrapping callLlm function that performs speculative decoding for target models.
|
|
175
|
+
*
|
|
176
|
+
* @param {Function} baseCallLlm
|
|
177
|
+
* @param {object} options
|
|
178
|
+
* @returns {Function}
|
|
179
|
+
*/
|
|
180
|
+
export function createSpeculativeCallLlm(baseCallLlm, options = {}) {
|
|
181
|
+
const { draftSlot, blockSize = 256, ctx, runId, onProgress } = options
|
|
182
|
+
if (!draftSlot || !draftSlot.model) return baseCallLlm
|
|
183
|
+
|
|
184
|
+
return async function speculativeCallLlm(opts) {
|
|
185
|
+
if (opts.provider === draftSlot.provider && opts.model === draftSlot.model) {
|
|
186
|
+
return baseCallLlm(opts)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const prompt = Array.isArray(opts.messages)
|
|
190
|
+
? opts.messages.map((m) => (typeof m === 'string' ? m : m.content || '')).join('\n')
|
|
191
|
+
: String(opts.prompt || '')
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
const res = await runSpeculativeVerification({
|
|
195
|
+
prompt,
|
|
196
|
+
draftSlot,
|
|
197
|
+
targetSlot: { provider: opts.provider, model: opts.model },
|
|
198
|
+
callLlm: baseCallLlm,
|
|
199
|
+
options: {
|
|
200
|
+
ctx,
|
|
201
|
+
runId,
|
|
202
|
+
onProgress,
|
|
203
|
+
blockSize,
|
|
204
|
+
signal: opts.signal,
|
|
205
|
+
},
|
|
206
|
+
})
|
|
207
|
+
return res.output
|
|
208
|
+
} catch (_err) {
|
|
209
|
+
return baseCallLlm(opts)
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|