@goodandready/dsh-moa 0.2.34 → 0.2.35
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/index.js +80 -279
- package/lib/moa-llm.js +73 -0
- package/lib/moa-schema.js +131 -0
- package/lib/moa-stream.js +51 -39
- package/lib/pricing.js +0 -3
- package/lib/routes.js +4 -0
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -1,165 +1,36 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
1
2
|
import { setLogger, logger } from './logger.js'
|
|
2
3
|
import { refreshCatalogInBackground } from './pricing.js'
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* 5. Native cost tracking, history logging and leaderboard analytics
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import z from '@deepseek-ai/schemastery'
|
|
15
|
-
|
|
16
|
-
// Ensure .volatile() helper exists on Schemastery schema prototype
|
|
17
|
-
try {
|
|
18
|
-
const schemaProto = Object.getPrototypeOf(z.boolean())
|
|
19
|
-
if (schemaProto && typeof schemaProto.volatile !== 'function') {
|
|
20
|
-
schemaProto.volatile = function () {
|
|
21
|
-
return typeof this.extra === 'function' ? this.extra('volatile', true) : this
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
} catch {
|
|
25
|
-
// safe fallback
|
|
26
|
-
}
|
|
27
|
-
import path from 'node:path'
|
|
4
|
+
import {
|
|
5
|
+
PriceRow,
|
|
6
|
+
ModelSlotSchema,
|
|
7
|
+
PresetSchema,
|
|
8
|
+
Config,
|
|
9
|
+
plainConfig,
|
|
10
|
+
} from './moa-schema.js'
|
|
11
|
+
import { createLlmCaller } from './moa-llm.js'
|
|
28
12
|
import {
|
|
29
13
|
parseMoACommand,
|
|
30
14
|
streamMoATurn,
|
|
31
15
|
} from './moa-runner.js'
|
|
32
|
-
|
|
33
16
|
import { registerMoaRoutes } from './routes.js'
|
|
34
17
|
import { resolvePresetForPrompt } from './moa-router.js'
|
|
35
|
-
|
|
36
18
|
import { createLiveCanvasClient } from './live-canvas.js'
|
|
37
19
|
import { registerPluginUpdater } from './updater.js'
|
|
38
20
|
|
|
39
21
|
export const name = '@goodandready/dsh-moa'
|
|
40
22
|
export { collectProjectContext, formatProjectContext } from './file-workspace.js'
|
|
41
23
|
export const inject = ['webServer', 'llm', 'sessions', 'tools']
|
|
42
|
-
|
|
43
24
|
export const NS = 'dsh-moa'
|
|
44
25
|
|
|
45
|
-
export
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
model: z.string().default(''),
|
|
54
|
-
role_persona: z.string().default(''),
|
|
55
|
-
temperature: z.number().default(-1),
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
export const PresetSchema = z.object({
|
|
59
|
-
name: z.string(),
|
|
60
|
-
enabled: z.boolean().default(true),
|
|
61
|
-
ask_clarifying_questions: z.boolean().default(true),
|
|
62
|
-
curator_synthesis: z.boolean().default(false),
|
|
63
|
-
stream_aggregator: z.boolean().default(true),
|
|
64
|
-
quorum_enabled: z.boolean().default(false),
|
|
65
|
-
grace_period_sec: z.number().default(10),
|
|
66
|
-
reference_models: z.array(ModelSlotSchema).default([]),
|
|
67
|
-
aggregator: ModelSlotSchema.default({ provider: '', model: '' }),
|
|
68
|
-
aggregator_fallbacks: z.array(ModelSlotSchema).default([]),
|
|
69
|
-
reference_temperature: z.number().default(0.6),
|
|
70
|
-
aggregator_temperature: z.number().default(0.4),
|
|
71
|
-
reference_timeout_sec: z.number().default(60),
|
|
72
|
-
aggregator_timeout_sec: z.number().default(180),
|
|
73
|
-
blind_evaluation: z.boolean().default(false),
|
|
74
|
-
peer_critique_enabled: z.boolean().default(false),
|
|
75
|
-
allow_candidate_override: z.boolean().default(false),
|
|
76
|
-
max_tokens: z.number().default(4096),
|
|
77
|
-
judge_criteria: z.string().default(''),
|
|
78
|
-
test_gate_enabled: z.boolean().default(false),
|
|
79
|
-
test_command: z.string().default(''),
|
|
80
|
-
test_gate_timeout_sec: z.number().default(15),
|
|
81
|
-
multi_judge_enabled: z.boolean().default(false),
|
|
82
|
-
judge_models: z.array(ModelSlotSchema).default([]),
|
|
83
|
-
judge_voting_strategy: z.string().default('majority'),
|
|
84
|
-
composite_merge_enabled: z.boolean().default(false),
|
|
85
|
-
smart_routing_enabled: z.boolean().default(false),
|
|
86
|
-
smart_routing_model: ModelSlotSchema.default({ provider: '', model: '' }),
|
|
87
|
-
budget_guard_enabled: z.boolean().default(false),
|
|
88
|
-
max_budget_usd: z.number().default(0),
|
|
89
|
-
budget_action: z.string().default('trim'),
|
|
90
|
-
temperature_gradient_enabled: z.boolean().default(false),
|
|
91
|
-
multi_turn_enabled: z.boolean().default(true),
|
|
92
|
-
report_generation_enabled: z.boolean().default(false),
|
|
93
|
-
local_fallback_enabled: z.boolean().default(false),
|
|
94
|
-
local_fallback_models: z.array(ModelSlotSchema).default([]),
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
export const Config = z.object({
|
|
98
|
-
enabled: z.boolean().default(true).volatile(),
|
|
99
|
-
default_preset: z.string().default('default').volatile(),
|
|
100
|
-
smart_routing_enabled: z.boolean().default(false).volatile(),
|
|
101
|
-
smart_routing_model: ModelSlotSchema.default({ provider: '', model: '' }).volatile(),
|
|
102
|
-
prices: z.dict(PriceRow).default({}).volatile(),
|
|
103
|
-
presets: z.array(PresetSchema).default([
|
|
104
|
-
{
|
|
105
|
-
name: 'default',
|
|
106
|
-
enabled: true,
|
|
107
|
-
ask_clarifying_questions: true,
|
|
108
|
-
reference_models: [
|
|
109
|
-
{ provider: '', model: '', role_persona: 'proposer' },
|
|
110
|
-
{ provider: '', model: '', role_persona: 'challenger' },
|
|
111
|
-
],
|
|
112
|
-
aggregator: { provider: '', model: '' },
|
|
113
|
-
reference_temperature: 0.6,
|
|
114
|
-
aggregator_temperature: 0.4,
|
|
115
|
-
max_tokens: 4096,
|
|
116
|
-
judge_criteria: '',
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
name: 'fast',
|
|
120
|
-
enabled: true,
|
|
121
|
-
ask_clarifying_questions: false,
|
|
122
|
-
reference_models: [
|
|
123
|
-
{ provider: '', model: '', role_persona: 'fast' },
|
|
124
|
-
],
|
|
125
|
-
aggregator: { provider: '', model: '' },
|
|
126
|
-
reference_temperature: 0.6,
|
|
127
|
-
aggregator_temperature: 0.2,
|
|
128
|
-
max_tokens: 4096,
|
|
129
|
-
judge_criteria: '',
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
name: 'deep-reasoning',
|
|
133
|
-
enabled: true,
|
|
134
|
-
ask_clarifying_questions: true,
|
|
135
|
-
reference_models: [
|
|
136
|
-
{ provider: '', model: '', role_persona: 'architect' },
|
|
137
|
-
{ provider: '', model: '', role_persona: 'critic' },
|
|
138
|
-
],
|
|
139
|
-
aggregator: { provider: '', model: '' },
|
|
140
|
-
reference_temperature: 0.6,
|
|
141
|
-
aggregator_temperature: 0.2,
|
|
142
|
-
max_tokens: 8192,
|
|
143
|
-
judge_criteria: '',
|
|
144
|
-
},
|
|
145
|
-
]).volatile(),
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
export function plainConfig(value) {
|
|
150
|
-
if (value && typeof value === 'object' && typeof value.get === 'function') {
|
|
151
|
-
return plainConfig(value.get())
|
|
152
|
-
}
|
|
153
|
-
if (Array.isArray(value)) {
|
|
154
|
-
return value.map(plainConfig)
|
|
155
|
-
}
|
|
156
|
-
if (value !== null && typeof value === 'object') {
|
|
157
|
-
return Object.fromEntries(
|
|
158
|
-
Object.entries(value).map(([key, child]) => [key, plainConfig(child)])
|
|
159
|
-
)
|
|
160
|
-
}
|
|
161
|
-
return value
|
|
162
|
-
}
|
|
26
|
+
export {
|
|
27
|
+
PriceRow,
|
|
28
|
+
ModelSlotSchema,
|
|
29
|
+
PresetSchema,
|
|
30
|
+
Config,
|
|
31
|
+
plainConfig,
|
|
32
|
+
} from './moa-schema.js'
|
|
33
|
+
export { createLlmCaller } from './moa-llm.js'
|
|
163
34
|
|
|
164
35
|
export function apply(ctx, config) {
|
|
165
36
|
if (ctx.logger) setLogger(ctx.logger)
|
|
@@ -214,76 +85,7 @@ export function apply(ctx, config) {
|
|
|
214
85
|
return localConfig
|
|
215
86
|
}
|
|
216
87
|
|
|
217
|
-
|
|
218
|
-
* Unified LLM dispatch function using ctx.llm.prepareCall / stream
|
|
219
|
-
*/
|
|
220
|
-
const callLlm = async ({ provider, model, messages, temperature = 0.6, maxTokens = 4096, timeoutMs, onStreamDelta }) => {
|
|
221
|
-
if (!ctx.llm) {
|
|
222
|
-
throw new Error('ctx.llm is not available in cordis context')
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
let prep
|
|
226
|
-
try {
|
|
227
|
-
// DSH 0.1.2-rc.1 API expects { provider, model } object
|
|
228
|
-
prep = await ctx.llm.prepareCall({ provider, model })
|
|
229
|
-
} catch (err) {
|
|
230
|
-
if (typeof ctx.llm.prepareCall === 'function') {
|
|
231
|
-
try {
|
|
232
|
-
prep = await ctx.llm.prepareCall(provider, model)
|
|
233
|
-
} catch {
|
|
234
|
-
throw err
|
|
235
|
-
}
|
|
236
|
-
} else {
|
|
237
|
-
throw err
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
if (!prep || typeof prep.stream !== 'function') {
|
|
241
|
-
throw new Error(`LLM provider/model ${provider}:${model} could not be prepared`)
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const effectiveTimeoutMs = typeof timeoutMs === 'number' && timeoutMs > 0 ? timeoutMs : 120000
|
|
245
|
-
const abortCtrl = new AbortController()
|
|
246
|
-
const timeoutId = setTimeout(() => abortCtrl.abort(new Error(`LLM call timeout after ${Math.round(effectiveTimeoutMs / 1000)}s`)), effectiveTimeoutMs)
|
|
247
|
-
timeoutId.unref?.()
|
|
248
|
-
|
|
249
|
-
try {
|
|
250
|
-
const stream = prep.stream({
|
|
251
|
-
messages,
|
|
252
|
-
temperature,
|
|
253
|
-
maxTokens,
|
|
254
|
-
signal: abortCtrl.signal,
|
|
255
|
-
})
|
|
256
|
-
|
|
257
|
-
let fullText = ''
|
|
258
|
-
let usageInfo = null
|
|
259
|
-
|
|
260
|
-
for await (const chunk of stream) {
|
|
261
|
-
if (chunk.type === 'text-delta' && typeof chunk.text === 'string') {
|
|
262
|
-
fullText += chunk.text
|
|
263
|
-
if (typeof onStreamDelta === 'function') {
|
|
264
|
-
onStreamDelta(chunk.text)
|
|
265
|
-
}
|
|
266
|
-
} else if (chunk.type === 'block-end' && chunk.block?.text) {
|
|
267
|
-
if (!fullText) fullText = chunk.block.text
|
|
268
|
-
} else if (chunk.type === 'usage' && chunk.usage) {
|
|
269
|
-
usageInfo = chunk.usage
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
clearTimeout(timeoutId)
|
|
274
|
-
return {
|
|
275
|
-
content: fullText,
|
|
276
|
-
text: fullText,
|
|
277
|
-
usage: usageInfo || {
|
|
278
|
-
inputTokens: Math.round(JSON.stringify(messages).length / 4),
|
|
279
|
-
outputTokens: Math.round(fullText.length / 4),
|
|
280
|
-
},
|
|
281
|
-
}
|
|
282
|
-
} catch (err) {
|
|
283
|
-
clearTimeout(timeoutId)
|
|
284
|
-
throw err
|
|
285
|
-
}
|
|
286
|
-
}
|
|
88
|
+
const callLlm = createLlmCaller(ctx)
|
|
287
89
|
|
|
288
90
|
// Optional Live Canvas preview client: the harness port is resolved per
|
|
289
91
|
// call and any failure degrades to "no preview link" (live-canvas is not
|
|
@@ -311,7 +113,6 @@ export function apply(ctx, config) {
|
|
|
311
113
|
})
|
|
312
114
|
|
|
313
115
|
// Session turn interceptor: route turn to real aggregator model & stream MoA synthesis
|
|
314
|
-
|
|
315
116
|
ctx.effect(() => {
|
|
316
117
|
return () => {
|
|
317
118
|
for (const entry of moaSessionTurns.values()) {
|
|
@@ -354,81 +155,81 @@ export function apply(ctx, config) {
|
|
|
354
155
|
|
|
355
156
|
ctx.effect(() => {
|
|
356
157
|
return ctx.on('agent/pre-step', async (event, next) => {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
158
|
+
const messages = event.messages || []
|
|
159
|
+
const lastUserMsg = [...messages].reverse().find((m) => m && m.role === 'user')
|
|
160
|
+
const userText = typeof lastUserMsg?.content === 'string'
|
|
161
|
+
? lastUserMsg.content.trim()
|
|
162
|
+
: (Array.isArray(lastUserMsg?.content)
|
|
163
|
+
? lastUserMsg.content.filter((p) => p.type === 'text').map((p) => p.text).join('\n').trim()
|
|
164
|
+
: '')
|
|
165
|
+
|
|
166
|
+
if (userText.startsWith('/moa')) {
|
|
167
|
+
const cfg = live()
|
|
168
|
+
if (cfg.enabled !== false) {
|
|
169
|
+
const parsed = parseMoACommand(userText, cfg.presets || []) || { prompt: userText, presetName: cfg.default_preset || 'default' }
|
|
170
|
+
const isExplicitPreset = userText.includes('--preset') || userText.includes('-p ')
|
|
171
|
+
if (!isExplicitPreset && (cfg.smart_routing_enabled || cfg.presets?.some((p) => p.smart_routing_enabled))) {
|
|
172
|
+
try {
|
|
173
|
+
const routed = await resolvePresetForPrompt({
|
|
174
|
+
prompt: parsed.prompt || userText,
|
|
175
|
+
presets: cfg.presets || [],
|
|
176
|
+
defaultPreset: parsed.presetName || cfg.default_preset || 'default',
|
|
177
|
+
routingModel: cfg.smart_routing_model,
|
|
178
|
+
callLlm,
|
|
179
|
+
enabled: true,
|
|
180
|
+
})
|
|
181
|
+
if (routed?.isAutoRouted && routed.presetName) {
|
|
182
|
+
parsed.presetName = routed.presetName
|
|
183
|
+
}
|
|
184
|
+
} catch {
|
|
185
|
+
// fallback silently
|
|
382
186
|
}
|
|
383
|
-
} catch {
|
|
384
|
-
// fallback silently
|
|
385
187
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
188
|
+
const targetPreset = (cfg.presets || []).find((p) => p.name === parsed.presetName) || cfg.presets?.[0]
|
|
189
|
+
const aggProvider = targetPreset?.aggregator?.provider || ''
|
|
190
|
+
const aggModel = targetPreset?.aggregator?.model || ''
|
|
191
|
+
const sessCwd = event.cwd || event.session?.cwd || (event.session?.path ? path.dirname(event.session.path) : undefined) || process.cwd()
|
|
192
|
+
|
|
193
|
+
const turnContext = {
|
|
194
|
+
targetPreset,
|
|
195
|
+
userPrompt: parsed.prompt || userText,
|
|
196
|
+
messages: messages.slice(0, -1),
|
|
197
|
+
cwd: sessCwd,
|
|
198
|
+
aggregator: { provider: aggProvider, model: aggModel },
|
|
199
|
+
callLlm,
|
|
200
|
+
prices: cfg.prices || {},
|
|
201
|
+
liveCanvas,
|
|
202
|
+
webServerPort: ctx.webServer?.port,
|
|
203
|
+
timeMachineEngine: ctx.get?.('timeMachineEngine') || ctx.timeMachineEngine,
|
|
204
|
+
}
|
|
404
205
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
206
|
+
if (event.signal) {
|
|
207
|
+
moaPendingTurns.set(event.signal, turnContext)
|
|
208
|
+
}
|
|
209
|
+
if (event.session?.id) {
|
|
210
|
+
setSessionTurn(event.session.id, turnContext)
|
|
211
|
+
}
|
|
410
212
|
}
|
|
411
213
|
}
|
|
412
|
-
}
|
|
413
214
|
|
|
414
|
-
|
|
215
|
+
return next()
|
|
415
216
|
})
|
|
416
217
|
}, 'dsh-moa: agent pre-step interceptor')
|
|
417
218
|
|
|
418
219
|
ctx.effect(() => {
|
|
419
220
|
return ctx.on('agent/request', async (payload, next) => {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
221
|
+
const resolved = await next()
|
|
222
|
+
const turnContext = (payload.signal ? moaPendingTurns.get(payload.signal) : null)
|
|
223
|
+
|| (payload.session?.id ? getSessionTurn(payload.session.id) : null)
|
|
423
224
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
225
|
+
if (turnContext) {
|
|
226
|
+
return {
|
|
227
|
+
...resolved,
|
|
228
|
+
provider: turnContext.aggregator.provider,
|
|
229
|
+
model: turnContext.aggregator.model,
|
|
230
|
+
}
|
|
429
231
|
}
|
|
430
|
-
|
|
431
|
-
return resolved
|
|
232
|
+
return resolved
|
|
432
233
|
})
|
|
433
234
|
}, 'dsh-moa: agent request interceptor')
|
|
434
235
|
|
package/lib/moa-llm.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified LLM Dispatcher using ctx.llm.prepareCall / stream
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function createLlmCaller(ctx) {
|
|
6
|
+
return async ({ provider, model, messages, temperature = 0.6, maxTokens = 4096, timeoutMs, onStreamDelta }) => {
|
|
7
|
+
if (!ctx.llm) {
|
|
8
|
+
throw new Error('ctx.llm is not available in cordis context')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let prep
|
|
12
|
+
try {
|
|
13
|
+
// DSH 0.1.2-rc.1 API expects { provider, model } object
|
|
14
|
+
prep = await ctx.llm.prepareCall({ provider, model })
|
|
15
|
+
} catch (err) {
|
|
16
|
+
if (typeof ctx.llm.prepareCall === 'function') {
|
|
17
|
+
try {
|
|
18
|
+
prep = await ctx.llm.prepareCall(provider, model)
|
|
19
|
+
} catch {
|
|
20
|
+
throw err
|
|
21
|
+
}
|
|
22
|
+
} else {
|
|
23
|
+
throw err
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (!prep || typeof prep.stream !== 'function') {
|
|
27
|
+
throw new Error(`LLM provider/model ${provider}:${model} could not be prepared`)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const effectiveTimeoutMs = typeof timeoutMs === 'number' && timeoutMs > 0 ? timeoutMs : 120000
|
|
31
|
+
const abortCtrl = new AbortController()
|
|
32
|
+
const timeoutId = setTimeout(() => abortCtrl.abort(new Error(`LLM call timeout after ${Math.round(effectiveTimeoutMs / 1000)}s`)), effectiveTimeoutMs)
|
|
33
|
+
timeoutId.unref?.()
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const stream = prep.stream({
|
|
37
|
+
messages,
|
|
38
|
+
temperature,
|
|
39
|
+
maxTokens,
|
|
40
|
+
signal: abortCtrl.signal,
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
let fullText = ''
|
|
44
|
+
let usageInfo = null
|
|
45
|
+
|
|
46
|
+
for await (const chunk of stream) {
|
|
47
|
+
if (chunk.type === 'text-delta' && typeof chunk.text === 'string') {
|
|
48
|
+
fullText += chunk.text
|
|
49
|
+
if (typeof onStreamDelta === 'function') {
|
|
50
|
+
onStreamDelta(chunk.text)
|
|
51
|
+
}
|
|
52
|
+
} else if (chunk.type === 'block-end' && chunk.block?.text) {
|
|
53
|
+
if (!fullText) fullText = chunk.block.text
|
|
54
|
+
} else if (chunk.type === 'usage' && chunk.usage) {
|
|
55
|
+
usageInfo = chunk.usage
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
clearTimeout(timeoutId)
|
|
60
|
+
return {
|
|
61
|
+
content: fullText,
|
|
62
|
+
text: fullText,
|
|
63
|
+
usage: usageInfo || {
|
|
64
|
+
inputTokens: Math.round(JSON.stringify(messages).length / 4),
|
|
65
|
+
outputTokens: Math.round(fullText.length / 4),
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
} catch (err) {
|
|
69
|
+
clearTimeout(timeoutId)
|
|
70
|
+
throw err
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import z from '@deepseek-ai/schemastery'
|
|
2
|
+
|
|
3
|
+
// Ensure .volatile() helper exists on Schemastery schema prototype
|
|
4
|
+
try {
|
|
5
|
+
const schemaProto = Object.getPrototypeOf(z.boolean())
|
|
6
|
+
if (schemaProto && typeof schemaProto.volatile !== 'function') {
|
|
7
|
+
schemaProto.volatile = function () {
|
|
8
|
+
return typeof this.extra === 'function' ? this.extra('volatile', true) : this
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
} catch {
|
|
12
|
+
// safe fallback
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const PriceRow = z.object({
|
|
16
|
+
input: z.number().default(0),
|
|
17
|
+
output: z.number().default(0),
|
|
18
|
+
cacheHit: z.number().default(0),
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
export const ModelSlotSchema = z.object({
|
|
22
|
+
provider: z.string().default(''),
|
|
23
|
+
model: z.string().default(''),
|
|
24
|
+
role_persona: z.string().default(''),
|
|
25
|
+
temperature: z.number().default(-1),
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
export const PresetSchema = z.object({
|
|
29
|
+
name: z.string(),
|
|
30
|
+
enabled: z.boolean().default(true),
|
|
31
|
+
ask_clarifying_questions: z.boolean().default(true),
|
|
32
|
+
curator_synthesis: z.boolean().default(false),
|
|
33
|
+
stream_aggregator: z.boolean().default(true),
|
|
34
|
+
quorum_enabled: z.boolean().default(false),
|
|
35
|
+
grace_period_sec: z.number().default(10),
|
|
36
|
+
reference_models: z.array(ModelSlotSchema).default([]),
|
|
37
|
+
aggregator: ModelSlotSchema.default({ provider: '', model: '' }),
|
|
38
|
+
aggregator_fallbacks: z.array(ModelSlotSchema).default([]),
|
|
39
|
+
reference_temperature: z.number().default(0.6),
|
|
40
|
+
aggregator_temperature: z.number().default(0.4),
|
|
41
|
+
reference_timeout_sec: z.number().default(60),
|
|
42
|
+
aggregator_timeout_sec: z.number().default(180),
|
|
43
|
+
blind_evaluation: z.boolean().default(false),
|
|
44
|
+
peer_critique_enabled: z.boolean().default(false),
|
|
45
|
+
allow_candidate_override: z.boolean().default(false),
|
|
46
|
+
max_tokens: z.number().default(4096),
|
|
47
|
+
judge_criteria: z.string().default(''),
|
|
48
|
+
test_gate_enabled: z.boolean().default(false),
|
|
49
|
+
test_command: z.string().default(''),
|
|
50
|
+
test_gate_timeout_sec: z.number().default(15),
|
|
51
|
+
multi_judge_enabled: z.boolean().default(false),
|
|
52
|
+
judge_models: z.array(ModelSlotSchema).default([]),
|
|
53
|
+
judge_voting_strategy: z.string().default('majority'),
|
|
54
|
+
composite_merge_enabled: z.boolean().default(false),
|
|
55
|
+
smart_routing_enabled: z.boolean().default(false),
|
|
56
|
+
smart_routing_model: ModelSlotSchema.default({ provider: '', model: '' }),
|
|
57
|
+
budget_guard_enabled: z.boolean().default(false),
|
|
58
|
+
max_budget_usd: z.number().default(0),
|
|
59
|
+
budget_action: z.string().default('trim'),
|
|
60
|
+
temperature_gradient_enabled: z.boolean().default(false),
|
|
61
|
+
multi_turn_enabled: z.boolean().default(true),
|
|
62
|
+
report_generation_enabled: z.boolean().default(false),
|
|
63
|
+
local_fallback_enabled: z.boolean().default(false),
|
|
64
|
+
local_fallback_models: z.array(ModelSlotSchema).default([]),
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
export const Config = z.object({
|
|
68
|
+
enabled: z.boolean().default(true).volatile(),
|
|
69
|
+
default_preset: z.string().default('default').volatile(),
|
|
70
|
+
smart_routing_enabled: z.boolean().default(false).volatile(),
|
|
71
|
+
smart_routing_model: ModelSlotSchema.default({ provider: '', model: '' }).volatile(),
|
|
72
|
+
prices: z.dict(PriceRow).default({}).volatile(),
|
|
73
|
+
presets: z.array(PresetSchema).default([
|
|
74
|
+
{
|
|
75
|
+
name: 'default',
|
|
76
|
+
enabled: true,
|
|
77
|
+
ask_clarifying_questions: true,
|
|
78
|
+
reference_models: [
|
|
79
|
+
{ provider: '', model: '', role_persona: 'proposer' },
|
|
80
|
+
{ provider: '', model: '', role_persona: 'challenger' },
|
|
81
|
+
],
|
|
82
|
+
aggregator: { provider: '', model: '' },
|
|
83
|
+
reference_temperature: 0.6,
|
|
84
|
+
aggregator_temperature: 0.4,
|
|
85
|
+
max_tokens: 4096,
|
|
86
|
+
judge_criteria: '',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'fast',
|
|
90
|
+
enabled: true,
|
|
91
|
+
ask_clarifying_questions: false,
|
|
92
|
+
reference_models: [
|
|
93
|
+
{ provider: '', model: '', role_persona: 'fast' },
|
|
94
|
+
],
|
|
95
|
+
aggregator: { provider: '', model: '' },
|
|
96
|
+
reference_temperature: 0.6,
|
|
97
|
+
aggregator_temperature: 0.2,
|
|
98
|
+
max_tokens: 4096,
|
|
99
|
+
judge_criteria: '',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: 'deep-reasoning',
|
|
103
|
+
enabled: true,
|
|
104
|
+
ask_clarifying_questions: true,
|
|
105
|
+
reference_models: [
|
|
106
|
+
{ provider: '', model: '', role_persona: 'architect' },
|
|
107
|
+
{ provider: '', model: '', role_persona: 'critic' },
|
|
108
|
+
],
|
|
109
|
+
aggregator: { provider: '', model: '' },
|
|
110
|
+
reference_temperature: 0.6,
|
|
111
|
+
aggregator_temperature: 0.2,
|
|
112
|
+
max_tokens: 8192,
|
|
113
|
+
judge_criteria: '',
|
|
114
|
+
},
|
|
115
|
+
]).volatile(),
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
export function plainConfig(value) {
|
|
119
|
+
if (value && typeof value === 'object' && typeof value.get === 'function') {
|
|
120
|
+
return plainConfig(value.get())
|
|
121
|
+
}
|
|
122
|
+
if (Array.isArray(value)) {
|
|
123
|
+
return value.map(plainConfig)
|
|
124
|
+
}
|
|
125
|
+
if (value !== null && typeof value === 'object') {
|
|
126
|
+
return Object.fromEntries(
|
|
127
|
+
Object.entries(value).map(([key, child]) => [key, plainConfig(child)])
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
return value
|
|
131
|
+
}
|
package/lib/moa-stream.js
CHANGED
|
@@ -13,53 +13,62 @@ export async function* streamMoATurn({ targetPreset, userPrompt, messages, callL
|
|
|
13
13
|
const signal = options?.signal
|
|
14
14
|
if (signal?.aborted) return
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const pushUpdate = (text) => {
|
|
24
|
-
queue.push({ type: 'text-delta', index: 0, text })
|
|
25
|
-
if (notify) {
|
|
26
|
-
notify()
|
|
27
|
-
notify = null
|
|
28
|
-
}
|
|
16
|
+
const internalAbortCtrl = new AbortController()
|
|
17
|
+
let combinedSignal = internalAbortCtrl.signal
|
|
18
|
+
if (typeof AbortSignal?.any === 'function' && (signal instanceof AbortSignal)) {
|
|
19
|
+
combinedSignal = AbortSignal.any([signal, internalAbortCtrl.signal])
|
|
20
|
+
} else if (signal && typeof signal.addEventListener === 'function') {
|
|
21
|
+
signal.addEventListener('abort', () => internalAbortCtrl.abort(), { once: true })
|
|
29
22
|
}
|
|
30
23
|
|
|
31
24
|
let done = false
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
webServerPort,
|
|
44
|
-
timeMachineEngine,
|
|
45
|
-
force,
|
|
46
|
-
signal,
|
|
47
|
-
})
|
|
48
|
-
.catch((err) => ({ error: err }))
|
|
49
|
-
.finally(() => {
|
|
50
|
-
done = true
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
yield { type: 'block-start', index: 0, blockType: 'text' }
|
|
28
|
+
yield { type: 'text-delta', index: 0, text: '🧠 *Mixture of Agents started...*\n\n' }
|
|
29
|
+
|
|
30
|
+
// Async push-queue for zero-latency live delta streaming
|
|
31
|
+
const queue = []
|
|
32
|
+
let notify = null
|
|
33
|
+
|
|
34
|
+
const pushUpdate = (text) => {
|
|
35
|
+
queue.push({ type: 'text-delta', index: 0, text })
|
|
51
36
|
if (notify) {
|
|
52
37
|
notify()
|
|
53
38
|
notify = null
|
|
54
39
|
}
|
|
55
|
-
}
|
|
40
|
+
}
|
|
56
41
|
|
|
57
|
-
|
|
58
|
-
|
|
42
|
+
const pipelinePromise = runMoAPipeline({
|
|
43
|
+
userPrompt,
|
|
44
|
+
messages,
|
|
45
|
+
preset: targetPreset,
|
|
46
|
+
callLlm,
|
|
47
|
+
cwd,
|
|
48
|
+
onProgress: pushUpdate,
|
|
49
|
+
onStreamDelta: (delta) => pushUpdate(delta),
|
|
50
|
+
prices,
|
|
51
|
+
historyFilePath,
|
|
52
|
+
liveCanvas,
|
|
53
|
+
webServerPort,
|
|
54
|
+
timeMachineEngine,
|
|
55
|
+
force,
|
|
56
|
+
signal: combinedSignal,
|
|
57
|
+
})
|
|
58
|
+
.catch((err) => ({ error: err }))
|
|
59
|
+
.finally(() => {
|
|
60
|
+
done = true
|
|
61
|
+
if (notify) {
|
|
62
|
+
notify()
|
|
63
|
+
notify = null
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
const startTime = Date.now()
|
|
68
|
+
let lastYieldTime = Date.now()
|
|
59
69
|
|
|
60
|
-
try {
|
|
61
70
|
while (!done || queue.length > 0) {
|
|
62
|
-
if (
|
|
71
|
+
if (combinedSignal.aborted) {
|
|
63
72
|
await cleanMoaWorkspaces(cwd)
|
|
64
73
|
return
|
|
65
74
|
}
|
|
@@ -86,7 +95,7 @@ export async function* streamMoATurn({ targetPreset, userPrompt, messages, callL
|
|
|
86
95
|
}
|
|
87
96
|
|
|
88
97
|
const result = await pipelinePromise
|
|
89
|
-
if (
|
|
98
|
+
if (combinedSignal.aborted) {
|
|
90
99
|
await cleanMoaWorkspaces(cwd)
|
|
91
100
|
return
|
|
92
101
|
}
|
|
@@ -115,6 +124,9 @@ export async function* streamMoATurn({ targetPreset, userPrompt, messages, callL
|
|
|
115
124
|
}
|
|
116
125
|
yield { type: 'finish', reason: { kind: 'stop' } }
|
|
117
126
|
} finally {
|
|
118
|
-
|
|
127
|
+
internalAbortCtrl.abort()
|
|
128
|
+
if (!done) {
|
|
129
|
+
await cleanMoaWorkspaces(cwd).catch(() => {})
|
|
130
|
+
}
|
|
119
131
|
}
|
|
120
132
|
}
|
package/lib/pricing.js
CHANGED
|
@@ -25,7 +25,6 @@ export function getDefaultCacheFile() {
|
|
|
25
25
|
return join(baseDir, 'storages', 'dsh-moa-catalog.json')
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
export const CACHE_FILE = getDefaultCacheFile()
|
|
29
28
|
export const REFRESH_INTERVAL_MS = 24 * 60 * 60 * 1000 // 24 hours
|
|
30
29
|
|
|
31
30
|
/**
|
|
@@ -34,8 +33,6 @@ export const REFRESH_INTERVAL_MS = 24 * 60 * 60 * 1000 // 24 hours
|
|
|
34
33
|
export const DIRECT_VENDOR_RATES = {
|
|
35
34
|
'deepseek-chat': { input: 0.14, output: 0.28, cacheHit: 0.014 },
|
|
36
35
|
'deepseek-reasoner': { input: 0.55, output: 2.19, cacheHit: 0.14 },
|
|
37
|
-
'deepseek-v4-flash': { input: 0.14, output: 0.28, cacheHit: 0.014 },
|
|
38
|
-
'deepseek-v4-pro': { input: 0.55, output: 2.19, cacheHit: 0.044 },
|
|
39
36
|
}
|
|
40
37
|
|
|
41
38
|
/** Default fallback rate for uncataloged models ($0.50 prompt / $1.50 completion per 1M tokens) */
|
package/lib/routes.js
CHANGED
|
@@ -198,9 +198,13 @@ ctx.effect(() => {
|
|
|
198
198
|
const raw = await readBody(req)
|
|
199
199
|
const payload = JSON.parse(raw)
|
|
200
200
|
const newConfig = {
|
|
201
|
+
...cfg,
|
|
201
202
|
enabled: typeof payload.enabled === 'boolean' ? payload.enabled : cfg.enabled !== false,
|
|
202
203
|
default_preset: payload.default_preset || cfg.default_preset || 'default',
|
|
203
204
|
presets: Array.isArray(payload.presets) ? payload.presets : (cfg.presets || []),
|
|
205
|
+
...(payload.prices !== undefined ? { prices: payload.prices } : {}),
|
|
206
|
+
...(payload.smart_routing_enabled !== undefined ? { smart_routing_enabled: payload.smart_routing_enabled } : {}),
|
|
207
|
+
...(payload.smart_routing_model !== undefined ? { smart_routing_model: payload.smart_routing_model } : {}),
|
|
204
208
|
}
|
|
205
209
|
|
|
206
210
|
// Validate the merged payload through the plugin schema before it
|