@defai.digital/discussion-domain 13.0.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 (82) hide show
  1. package/LICENSE +214 -0
  2. package/dist/consensus/index.d.ts +23 -0
  3. package/dist/consensus/index.d.ts.map +1 -0
  4. package/dist/consensus/index.js +45 -0
  5. package/dist/consensus/index.js.map +1 -0
  6. package/dist/consensus/moderator.d.ts +15 -0
  7. package/dist/consensus/moderator.d.ts.map +1 -0
  8. package/dist/consensus/moderator.js +201 -0
  9. package/dist/consensus/moderator.js.map +1 -0
  10. package/dist/consensus/synthesis.d.ts +15 -0
  11. package/dist/consensus/synthesis.d.ts.map +1 -0
  12. package/dist/consensus/synthesis.js +161 -0
  13. package/dist/consensus/synthesis.js.map +1 -0
  14. package/dist/consensus/voting.d.ts +17 -0
  15. package/dist/consensus/voting.d.ts.map +1 -0
  16. package/dist/consensus/voting.js +168 -0
  17. package/dist/consensus/voting.js.map +1 -0
  18. package/dist/executor.d.ts +73 -0
  19. package/dist/executor.d.ts.map +1 -0
  20. package/dist/executor.js +223 -0
  21. package/dist/executor.js.map +1 -0
  22. package/dist/index.d.ts +16 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +21 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/patterns/critique.d.ts +20 -0
  27. package/dist/patterns/critique.d.ts.map +1 -0
  28. package/dist/patterns/critique.js +338 -0
  29. package/dist/patterns/critique.js.map +1 -0
  30. package/dist/patterns/debate.d.ts +20 -0
  31. package/dist/patterns/debate.d.ts.map +1 -0
  32. package/dist/patterns/debate.js +236 -0
  33. package/dist/patterns/debate.js.map +1 -0
  34. package/dist/patterns/index.d.ts +25 -0
  35. package/dist/patterns/index.d.ts.map +1 -0
  36. package/dist/patterns/index.js +49 -0
  37. package/dist/patterns/index.js.map +1 -0
  38. package/dist/patterns/round-robin.d.ts +13 -0
  39. package/dist/patterns/round-robin.d.ts.map +1 -0
  40. package/dist/patterns/round-robin.js +160 -0
  41. package/dist/patterns/round-robin.js.map +1 -0
  42. package/dist/patterns/synthesis.d.ts +20 -0
  43. package/dist/patterns/synthesis.d.ts.map +1 -0
  44. package/dist/patterns/synthesis.js +250 -0
  45. package/dist/patterns/synthesis.js.map +1 -0
  46. package/dist/patterns/voting.d.ts +19 -0
  47. package/dist/patterns/voting.d.ts.map +1 -0
  48. package/dist/patterns/voting.js +186 -0
  49. package/dist/patterns/voting.js.map +1 -0
  50. package/dist/prompts/index.d.ts +7 -0
  51. package/dist/prompts/index.d.ts.map +1 -0
  52. package/dist/prompts/index.js +21 -0
  53. package/dist/prompts/index.js.map +1 -0
  54. package/dist/prompts/templates.d.ts +55 -0
  55. package/dist/prompts/templates.d.ts.map +1 -0
  56. package/dist/prompts/templates.js +346 -0
  57. package/dist/prompts/templates.js.map +1 -0
  58. package/dist/provider-bridge.d.ts +115 -0
  59. package/dist/provider-bridge.d.ts.map +1 -0
  60. package/dist/provider-bridge.js +215 -0
  61. package/dist/provider-bridge.js.map +1 -0
  62. package/dist/types.d.ts +201 -0
  63. package/dist/types.d.ts.map +1 -0
  64. package/dist/types.js +102 -0
  65. package/dist/types.js.map +1 -0
  66. package/package.json +48 -0
  67. package/src/consensus/index.ts +52 -0
  68. package/src/consensus/moderator.ts +242 -0
  69. package/src/consensus/synthesis.ts +202 -0
  70. package/src/consensus/voting.ts +221 -0
  71. package/src/executor.ts +338 -0
  72. package/src/index.ts +69 -0
  73. package/src/patterns/critique.ts +465 -0
  74. package/src/patterns/debate.ts +340 -0
  75. package/src/patterns/index.ts +56 -0
  76. package/src/patterns/round-robin.ts +223 -0
  77. package/src/patterns/synthesis.ts +353 -0
  78. package/src/patterns/voting.ts +266 -0
  79. package/src/prompts/index.ts +41 -0
  80. package/src/prompts/templates.ts +381 -0
  81. package/src/provider-bridge.ts +346 -0
  82. package/src/types.ts +375 -0
@@ -0,0 +1,465 @@
1
+ /**
2
+ * Critique Pattern Executor
3
+ *
4
+ * One model proposes, others critique, author refines.
5
+ *
6
+ * Flow:
7
+ * 1. Round 1: First provider proposes solution
8
+ * 2. Round 2: Other providers critique the proposal
9
+ * 3. Round 3: Original proposer revises based on feedback
10
+ * 4. (Optional) Additional critique-revision cycles
11
+ */
12
+
13
+ import type { DiscussionRound, DebateRole } from '@defai.digital/contracts';
14
+ import type {
15
+ PatternExecutor,
16
+ PatternExecutionContext,
17
+ PatternExecutionResult,
18
+ } from '../types.js';
19
+ import {
20
+ CRITIQUE_PROPOSAL,
21
+ CRITIQUE_REVIEW,
22
+ CRITIQUE_REVISION,
23
+ interpolate,
24
+ formatPreviousResponses,
25
+ getProviderSystemPrompt,
26
+ } from '../prompts/templates.js';
27
+
28
+ // Local type for discussion DiscussionProviderResponse (avoids conflict with provider/v1 DiscussionProviderResponse)
29
+ interface DiscussionProviderResponse {
30
+ provider: string;
31
+ content: string;
32
+ round: number;
33
+ role?: DebateRole | undefined;
34
+ confidence?: number | undefined;
35
+ vote?: string | undefined;
36
+ timestamp: string;
37
+ durationMs: number;
38
+ tokenCount?: number | undefined;
39
+ truncated?: boolean | undefined;
40
+ error?: string | undefined;
41
+ }
42
+
43
+ export class CritiquePattern implements PatternExecutor {
44
+ readonly pattern = 'critique' as const;
45
+
46
+ async execute(context: PatternExecutionContext): Promise<PatternExecutionResult> {
47
+ const startTime = Date.now();
48
+ const { config, providerExecutor, availableProviders, abortSignal, onProgress } = context;
49
+
50
+ const rounds: DiscussionRound[] = [];
51
+ const participatingProviders = new Set<string>();
52
+ const failedProviders = new Set<string>();
53
+
54
+ // Filter to available providers
55
+ const providers = config.providers.filter(p => availableProviders.includes(p));
56
+
57
+ if (providers.length < config.minProviders) {
58
+ return {
59
+ rounds: [],
60
+ participatingProviders: [],
61
+ failedProviders: config.providers.filter(p => !availableProviders.includes(p)),
62
+ totalDurationMs: Date.now() - startTime,
63
+ success: false,
64
+ error: `Only ${providers.length} providers available, need ${config.minProviders}`,
65
+ };
66
+ }
67
+
68
+ // First provider is the proposer (guaranteed to exist since we checked providers.length >= minProviders)
69
+ const proposerId = providers[0]!;
70
+ const critiquers = providers.slice(1);
71
+
72
+ let currentProposal = '';
73
+
74
+ // Round 1: Initial proposal
75
+ onProgress?.({
76
+ type: 'round_start',
77
+ round: 1,
78
+ message: `${proposerId} creating initial proposal`,
79
+ timestamp: new Date().toISOString(),
80
+ });
81
+
82
+ const proposalRound = await this.executeProposalRound(
83
+ 1,
84
+ proposerId,
85
+ config,
86
+ providerExecutor,
87
+ abortSignal,
88
+ onProgress
89
+ );
90
+
91
+ rounds.push(proposalRound.round);
92
+ if (proposalRound.success) {
93
+ participatingProviders.add(proposerId);
94
+ currentProposal = proposalRound.content;
95
+ } else {
96
+ failedProviders.add(proposerId);
97
+ return {
98
+ rounds,
99
+ participatingProviders: Array.from(participatingProviders),
100
+ failedProviders: Array.from(failedProviders),
101
+ totalDurationMs: Date.now() - startTime,
102
+ success: false,
103
+ error: 'Proposer failed to create initial proposal',
104
+ };
105
+ }
106
+
107
+ // Critique-Revision cycles
108
+ const cycles = Math.floor((config.rounds - 1) / 2);
109
+ let allCritiques: DiscussionProviderResponse[] = [];
110
+
111
+ for (let cycle = 0; cycle < cycles; cycle++) {
112
+ if (abortSignal?.aborted) break;
113
+
114
+ const critiqueRoundNum = 2 + cycle * 2;
115
+ const revisionRoundNum = critiqueRoundNum + 1;
116
+
117
+ // Critique round
118
+ onProgress?.({
119
+ type: 'round_start',
120
+ round: critiqueRoundNum,
121
+ message: 'Gathering critiques',
122
+ timestamp: new Date().toISOString(),
123
+ });
124
+
125
+ const critiqueRound = await this.executeCritiqueRound(
126
+ critiqueRoundNum,
127
+ critiquers.filter(c => !failedProviders.has(c)),
128
+ currentProposal,
129
+ config,
130
+ providerExecutor,
131
+ abortSignal,
132
+ onProgress
133
+ );
134
+
135
+ rounds.push(critiqueRound.round);
136
+ critiqueRound.succeeded.forEach(p => participatingProviders.add(p));
137
+ critiqueRound.failed.forEach(p => failedProviders.add(p));
138
+ allCritiques = critiqueRound.round.responses;
139
+
140
+ onProgress?.({
141
+ type: 'round_complete',
142
+ round: critiqueRoundNum,
143
+ message: `Received ${critiqueRound.succeeded.length} critiques`,
144
+ timestamp: new Date().toISOString(),
145
+ });
146
+
147
+ // Check if we have enough critiques
148
+ if (critiqueRound.succeeded.length === 0) {
149
+ break; // No valid critiques to incorporate
150
+ }
151
+
152
+ // Revision round
153
+ if (revisionRoundNum <= config.rounds) {
154
+ onProgress?.({
155
+ type: 'round_start',
156
+ round: revisionRoundNum,
157
+ message: `${proposerId} revising proposal`,
158
+ timestamp: new Date().toISOString(),
159
+ });
160
+
161
+ const revisionRound = await this.executeRevisionRound(
162
+ revisionRoundNum,
163
+ proposerId,
164
+ currentProposal,
165
+ allCritiques,
166
+ config,
167
+ providerExecutor,
168
+ abortSignal,
169
+ onProgress
170
+ );
171
+
172
+ rounds.push(revisionRound.round);
173
+ if (revisionRound.success) {
174
+ currentProposal = revisionRound.content;
175
+ }
176
+
177
+ onProgress?.({
178
+ type: 'round_complete',
179
+ round: revisionRoundNum,
180
+ message: 'Proposal revised',
181
+ timestamp: new Date().toISOString(),
182
+ });
183
+ }
184
+ }
185
+
186
+ return {
187
+ rounds,
188
+ participatingProviders: Array.from(participatingProviders),
189
+ failedProviders: Array.from(failedProviders),
190
+ totalDurationMs: Date.now() - startTime,
191
+ success: participatingProviders.size >= config.minProviders,
192
+ };
193
+ }
194
+
195
+ private async executeProposalRound(
196
+ roundNum: number,
197
+ proposerId: string,
198
+ config: PatternExecutionContext['config'],
199
+ providerExecutor: PatternExecutionContext['providerExecutor'],
200
+ abortSignal?: AbortSignal,
201
+ onProgress?: PatternExecutionContext['onProgress']
202
+ ): Promise<{ round: DiscussionRound; success: boolean; content: string }> {
203
+ const roundStart = Date.now();
204
+
205
+ onProgress?.({
206
+ type: 'provider_start',
207
+ round: roundNum,
208
+ provider: proposerId,
209
+ timestamp: new Date().toISOString(),
210
+ });
211
+
212
+ try {
213
+ const prompt = interpolate(CRITIQUE_PROPOSAL, {
214
+ topic: config.prompt,
215
+ context: config.context || '',
216
+ });
217
+
218
+ const result = await providerExecutor.execute({
219
+ providerId: proposerId,
220
+ prompt,
221
+ systemPrompt: getProviderSystemPrompt(proposerId),
222
+ temperature: config.temperature,
223
+ timeoutMs: config.providerTimeout,
224
+ abortSignal,
225
+ });
226
+
227
+ const response: DiscussionProviderResponse = {
228
+ provider: proposerId,
229
+ content: result.success ? result.content || '' : '',
230
+ round: roundNum,
231
+ timestamp: new Date().toISOString(),
232
+ durationMs: result.durationMs,
233
+ tokenCount: result.tokenCount,
234
+ error: result.success ? undefined : result.error,
235
+ };
236
+
237
+ onProgress?.({
238
+ type: 'provider_complete',
239
+ round: roundNum,
240
+ provider: proposerId,
241
+ timestamp: new Date().toISOString(),
242
+ });
243
+
244
+ onProgress?.({
245
+ type: 'round_complete',
246
+ round: roundNum,
247
+ timestamp: new Date().toISOString(),
248
+ });
249
+
250
+ return {
251
+ round: {
252
+ roundNumber: roundNum,
253
+ responses: [response],
254
+ durationMs: Date.now() - roundStart,
255
+ },
256
+ success: result.success,
257
+ content: result.content || '',
258
+ };
259
+
260
+ } catch (error) {
261
+ const errorMessage = error instanceof Error ? error.message : String(error);
262
+
263
+ return {
264
+ round: {
265
+ roundNumber: roundNum,
266
+ responses: [{
267
+ provider: proposerId,
268
+ content: '',
269
+ round: roundNum,
270
+ timestamp: new Date().toISOString(),
271
+ durationMs: Date.now() - roundStart,
272
+ error: errorMessage,
273
+ }],
274
+ durationMs: Date.now() - roundStart,
275
+ },
276
+ success: false,
277
+ content: '',
278
+ };
279
+ }
280
+ }
281
+
282
+ private async executeCritiqueRound(
283
+ roundNum: number,
284
+ critiquers: string[],
285
+ proposal: string,
286
+ config: PatternExecutionContext['config'],
287
+ providerExecutor: PatternExecutionContext['providerExecutor'],
288
+ abortSignal?: AbortSignal,
289
+ onProgress?: PatternExecutionContext['onProgress']
290
+ ): Promise<{ round: DiscussionRound; succeeded: string[]; failed: string[] }> {
291
+ const roundStart = Date.now();
292
+ const succeeded: string[] = [];
293
+ const failed: string[] = [];
294
+
295
+ const prompt = interpolate(CRITIQUE_REVIEW, {
296
+ topic: config.prompt,
297
+ proposal,
298
+ });
299
+
300
+ // Execute critiques in parallel
301
+ const promises = critiquers.map(async (critiquerId) => {
302
+ if (abortSignal?.aborted) return null;
303
+
304
+ onProgress?.({
305
+ type: 'provider_start',
306
+ round: roundNum,
307
+ provider: critiquerId,
308
+ timestamp: new Date().toISOString(),
309
+ });
310
+
311
+ const responseStart = Date.now();
312
+
313
+ try {
314
+ const result = await providerExecutor.execute({
315
+ providerId: critiquerId,
316
+ prompt,
317
+ systemPrompt: getProviderSystemPrompt(critiquerId),
318
+ temperature: config.temperature,
319
+ timeoutMs: config.providerTimeout,
320
+ abortSignal,
321
+ });
322
+
323
+ if (result.success) {
324
+ succeeded.push(critiquerId);
325
+ } else {
326
+ failed.push(critiquerId);
327
+ }
328
+
329
+ onProgress?.({
330
+ type: 'provider_complete',
331
+ round: roundNum,
332
+ provider: critiquerId,
333
+ timestamp: new Date().toISOString(),
334
+ });
335
+
336
+ return {
337
+ provider: critiquerId,
338
+ content: result.success ? result.content || '' : '',
339
+ round: roundNum,
340
+ timestamp: new Date().toISOString(),
341
+ durationMs: result.durationMs,
342
+ tokenCount: result.tokenCount,
343
+ error: result.success ? undefined : result.error,
344
+ } as DiscussionProviderResponse;
345
+
346
+ } catch (error) {
347
+ const errorMessage = error instanceof Error ? error.message : String(error);
348
+ failed.push(critiquerId);
349
+
350
+ return {
351
+ provider: critiquerId,
352
+ content: '',
353
+ round: roundNum,
354
+ timestamp: new Date().toISOString(),
355
+ durationMs: Date.now() - responseStart,
356
+ error: errorMessage,
357
+ } as DiscussionProviderResponse;
358
+ }
359
+ });
360
+
361
+ const responses = await Promise.all(promises);
362
+ const validResponses = responses.filter((r): r is DiscussionProviderResponse => r !== null);
363
+
364
+ return {
365
+ round: {
366
+ roundNumber: roundNum,
367
+ responses: validResponses,
368
+ durationMs: Date.now() - roundStart,
369
+ },
370
+ succeeded,
371
+ failed,
372
+ };
373
+ }
374
+
375
+ private async executeRevisionRound(
376
+ roundNum: number,
377
+ proposerId: string,
378
+ originalProposal: string,
379
+ critiques: DiscussionProviderResponse[],
380
+ config: PatternExecutionContext['config'],
381
+ providerExecutor: PatternExecutionContext['providerExecutor'],
382
+ abortSignal?: AbortSignal,
383
+ onProgress?: PatternExecutionContext['onProgress']
384
+ ): Promise<{ round: DiscussionRound; success: boolean; content: string }> {
385
+ const roundStart = Date.now();
386
+
387
+ onProgress?.({
388
+ type: 'provider_start',
389
+ round: roundNum,
390
+ provider: proposerId,
391
+ timestamp: new Date().toISOString(),
392
+ });
393
+
394
+ try {
395
+ const formattedCritiques = formatPreviousResponses(
396
+ critiques.map(c => ({
397
+ provider: c.provider,
398
+ content: c.content,
399
+ }))
400
+ );
401
+
402
+ const prompt = interpolate(CRITIQUE_REVISION, {
403
+ topic: config.prompt,
404
+ originalProposal,
405
+ critiques: formattedCritiques,
406
+ });
407
+
408
+ const result = await providerExecutor.execute({
409
+ providerId: proposerId,
410
+ prompt,
411
+ systemPrompt: getProviderSystemPrompt(proposerId),
412
+ temperature: config.temperature,
413
+ timeoutMs: config.providerTimeout,
414
+ abortSignal,
415
+ });
416
+
417
+ const response: DiscussionProviderResponse = {
418
+ provider: proposerId,
419
+ content: result.success ? result.content || '' : '',
420
+ round: roundNum,
421
+ timestamp: new Date().toISOString(),
422
+ durationMs: result.durationMs,
423
+ tokenCount: result.tokenCount,
424
+ error: result.success ? undefined : result.error,
425
+ };
426
+
427
+ onProgress?.({
428
+ type: 'provider_complete',
429
+ round: roundNum,
430
+ provider: proposerId,
431
+ timestamp: new Date().toISOString(),
432
+ });
433
+
434
+ return {
435
+ round: {
436
+ roundNumber: roundNum,
437
+ responses: [response],
438
+ durationMs: Date.now() - roundStart,
439
+ },
440
+ success: result.success,
441
+ content: result.content || '',
442
+ };
443
+
444
+ } catch (error) {
445
+ const errorMessage = error instanceof Error ? error.message : String(error);
446
+
447
+ return {
448
+ round: {
449
+ roundNumber: roundNum,
450
+ responses: [{
451
+ provider: proposerId,
452
+ content: '',
453
+ round: roundNum,
454
+ timestamp: new Date().toISOString(),
455
+ durationMs: Date.now() - roundStart,
456
+ error: errorMessage,
457
+ }],
458
+ durationMs: Date.now() - roundStart,
459
+ },
460
+ success: false,
461
+ content: '',
462
+ };
463
+ }
464
+ }
465
+ }