@goodandready/dsh-moa 0.2.12 → 0.2.14
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 +48 -2
- package/README.ru.md +287 -0
- package/README.zh.md +243 -0
- package/docs/README.ru.md +47 -1
- package/docs/README.zh.md +11 -1
- package/docs/design/DESIGN.md +4 -0
- package/docs/plans/59-power-pack-plan.md +73 -0
- package/lib/client.js +210 -3
- package/lib/file-workspace.js +53 -5
- package/lib/history.js +56 -2
- package/lib/index.js +86 -2
- package/lib/moa-parser.js +27 -3
- package/lib/moa-prompts.js +75 -10
- package/lib/moa-runner.js +68 -57
- package/package.json +1 -1
package/docs/README.ru.md
CHANGED
|
@@ -16,11 +16,21 @@
|
|
|
16
16
|
</p>
|
|
17
17
|
|
|
18
18
|
<p align="center">
|
|
19
|
-
<a href="
|
|
19
|
+
<a href="README.md"><b>🇬🇧 English</b></a> •
|
|
20
20
|
<a href="README.ru.md"><b>🇷🇺 Русский</b></a> •
|
|
21
21
|
<a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
|
|
22
22
|
</p>
|
|
23
23
|
|
|
24
|
+
<table align="center">
|
|
25
|
+
<tr>
|
|
26
|
+
<td align="center">
|
|
27
|
+
⭐ <strong>Если вам нравится этот плагин, поставьте ему звезду на GitHub</strong> — это покажет мне, что плагин вам полезен, и будет мотивировать меня развивать его дальше.
|
|
28
|
+
<br><br>
|
|
29
|
+
🐛 <strong>Если вы нашли баг или хотите предложить новый функционал</strong>, создайте issue на GitHub на любом языке — я рассмотрю ваше предложение и реализую полезные идеи в одной из следующих версий плагина.
|
|
30
|
+
</td>
|
|
31
|
+
</tr>
|
|
32
|
+
</table>
|
|
33
|
+
|
|
24
34
|
</div>
|
|
25
35
|
|
|
26
36
|
---
|
|
@@ -143,8 +153,44 @@ dsh plugin --profile web add @goodandready/dsh-moa
|
|
|
143
153
|
|
|
144
154
|
---
|
|
145
155
|
|
|
156
|
+
## ⚡ 10 встроенных пресетов и инженерные персоны
|
|
157
|
+
|
|
158
|
+
В версии v0.2.13 добавлены 10 специализированных пресетов под реальные задачи разработки:
|
|
159
|
+
|
|
160
|
+
| Имя пресета | Назначение | Судья по умолчанию | Рецензия (Round 2) | Слепая оценка |
|
|
161
|
+
| :--- | :--- | :--- | :---: | :---: |
|
|
162
|
+
| `default` | Сбалансированная генерация | `codex:gpt-5.6-sol` | Опционально | Выкл |
|
|
163
|
+
| `code-review` | Глубокое взаимное ревью кода | `codex:gpt-5.6-sol` | Вкл | Вкл |
|
|
164
|
+
| `fast-audit` | Сверхбыстрый экспресс-аудит (Fast Mode) | `codex:gpt-5.6-sol` | Выкл | Выкл |
|
|
165
|
+
| `deep-architect` | Проектирование распределенных систем | `codex:gpt-5.6-sol` | Вкл | Выкл |
|
|
166
|
+
| `bug-hunter` | Поиск скрытых багов и граничных случаев | `codex:gpt-5.6-sol` | Вкл | Выкл |
|
|
167
|
+
| `refactor-cleanup` | Чистка легаси и удаление оверинжиниринга | `codex:gpt-5.6-sol` | Выкл | Выкл |
|
|
168
|
+
| `frontend-ui` | Качественные адаптивные веб-интерфейсы | `codex:gpt-5.6-sol` | Выкл | Выкл |
|
|
169
|
+
| `security-audit` | Анализ уязвимостей и санитайзинг | `codex:gpt-5.6-sol` | Вкл | Вкл |
|
|
170
|
+
| `math-logic` | Алгоритмы, математическая логика и доказательства | `codex:gpt-5.6-sol` | Вкл | Выкл |
|
|
171
|
+
| `creative-brainstorm`| Нестандартные идеи и дивергентный поиск | `codex:gpt-5.6-sol` | Выкл | Выкл |
|
|
172
|
+
|
|
173
|
+
### Инженерные персоны кандидатов (`role_persona`)
|
|
174
|
+
Задавайте кандидатам четкие роли для максимального разнообразия подходов:
|
|
175
|
+
- **`minimalist` (Ponytail)**: решение на стандартной библиотеке, 0 лишних зависимостей, минимальный код.
|
|
176
|
+
- **`robustness`**: защитное программирование, валидация границ, идемпотентность, устойчивость к сбоям.
|
|
177
|
+
- **`performance`**: минимальная алгоритмическая сложность, экономия памяти, zero-copy.
|
|
178
|
+
- **`tester`**: test-driven архитектура, 100% покрытие веток, четкие ассерты.
|
|
179
|
+
- **`general`**: сбалансированный подход общего назначения.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 🤝 Консилиум Раунд 2, Синтаксический гейт и Override
|
|
184
|
+
|
|
185
|
+
- **Консилиум (Раунд 2)**: флаг `peer_critique_enabled: true` включает этап взаимного ревью, в котором кандидаты изучают предложения оппонентов и дорабатывают код перед финальным судейством.
|
|
186
|
+
- **Синтаксический гейт**: мгновенная валидация JS/JSON через `node:vm` и `JSON.parse`. Если в коде кандидата есть опечатка, он помечается `[⚠️ Syntax Warning]`, а судья получает жесткую директиву: *если архитектура кандидата превосходит конкурентов, исправить синтаксис в финальном решении и присудить ему победу*.
|
|
187
|
+
- **Пользовательский выбор кандидата (Override)**: флаг `allow_candidate_override: true` сохраняет папки `.moa/candidate-N/`. Любой кандидат может быть повышен в проект командой `/moa promote <runId> <candidateIndex>` или кнопкой в карточке настроек.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
146
191
|
## ⚙️ Конфигурация (`settings.yaml`)
|
|
147
192
|
|
|
193
|
+
|
|
148
194
|
Настройка пресетов и пайплайнов моделей доступна в `settings.yaml` или через интерфейс (Настройки → Плагины → Mixture of Agents):
|
|
149
195
|
|
|
150
196
|
```yaml
|
package/docs/README.zh.md
CHANGED
|
@@ -16,11 +16,21 @@
|
|
|
16
16
|
</p>
|
|
17
17
|
|
|
18
18
|
<p align="center">
|
|
19
|
-
<a href="
|
|
19
|
+
<a href="README.md"><b>🇬🇧 English</b></a> •
|
|
20
20
|
<a href="README.ru.md"><b>🇷🇺 Русский</b></a> •
|
|
21
21
|
<a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
|
|
22
22
|
</p>
|
|
23
23
|
|
|
24
|
+
<table align="center">
|
|
25
|
+
<tr>
|
|
26
|
+
<td align="center">
|
|
27
|
+
⭐ <strong>如果您喜欢这个插件,请在 GitHub 上为它点亮 Star</strong> — 这能让我知道插件对您有用,并鼓励我继续开发和维护它。
|
|
28
|
+
<br><br>
|
|
29
|
+
🐛 <strong>如果您发现 Bug 或希望增加功能</strong>,请使用任意语言在 GitHub 上提交 Issue — 我会评估您的建议,并在后续版本中实现有价值的改进。
|
|
30
|
+
</td>
|
|
31
|
+
</tr>
|
|
32
|
+
</table>
|
|
33
|
+
|
|
24
34
|
</div>
|
|
25
35
|
|
|
26
36
|
---
|
package/docs/design/DESIGN.md
CHANGED
|
@@ -67,3 +67,7 @@
|
|
|
67
67
|
- 2026-09-12 — Реализация кураторского синтеза (`curator_synthesis`), строгой рубрики антипаттернов (`ANTIPATTERNS_RUBRIC`), живого потокового стриминга куратора (`stream_aggregator`), кворума кандидатов с льготным периодом (`quorum_enabled`, `grace_period_sec`), авторетрая транзиентных ошибок (`callWithTransientRetry`) и цепочки запасных судей (`aggregator_fallbacks`). Все опции конфигурируются в пресетах с сохранением 100% обратной совместимости.
|
|
68
68
|
|
|
69
69
|
- 2026-09-12 (вечер) — Декомпозиция lib/moa-runner.js на специализированные модули (`lib/moa-prompts.js`, `lib/moa-parser.js`, `lib/moa-runner.js`) с соблюдением канонического лимита <= 800 строк. Реализация автоматической ротации истории при превышении 10 МБ (`rotateHistoryFileIfNeeded`) и неблокирующего асинхронного сохранения (`recordMoaRunAsync`). Настраиваемые таймауты кандидатов и агрегатора (`reference_timeout_sec`, `aggregator_timeout_sec`).
|
|
70
|
+
|
|
71
|
+
- 2026-09-13 — Расширение функционала MoA (Gitea Issue #59): интерактивный селектор альтернативного кандидата в чате (User Candidate Override: allow_candidate_override), пакет из 10 специализированных пресетов с ролями кандидатов (role_persona), локальный гейт синтаксической проверки (Syntax Pre-check Gate) с директивой автоисправления синтаксиса судьёй, режим взаимного рецензирования («Консилиум» / Peer Critique Round 2: peer_critique_enabled), фильтрация лидерборда по пресетам и выгрузка телеметрии в CSV/JSON.
|
|
72
|
+
|
|
73
|
+
- 2026-09-13 — Приведение локализации плагина в строгое соответствие со стандартами dhs-plugin-release-workflow и dsh-plugin-authoring (Gitea Issue #61): добавление полного китайского словаря (zh) в lib/client.js наряду с каноническим английским (en), удаление захардкоженных русских строк в серверной части (lib/moa-parser.js, lib/moa-runner.js), интернационализация эвристик в lib/file-workspace.js и lib/moa-prompts.js (en + zh), создание issue в goodandready/dsh-russian-lang для русификации.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Plan 59: MoA Power Pack v0.2.13
|
|
2
|
+
|
|
3
|
+
## Objectives
|
|
4
|
+
Implement the 5 approved feature expansions for `@goodandready/dsh-moa`:
|
|
5
|
+
1. User Candidate Override (`allow_candidate_override: boolean`).
|
|
6
|
+
2. 10 specialized built-in presets + candidate role personas (`role_persona`).
|
|
7
|
+
3. Syntax Pre-check Gate with Judge Auto-Fix Directive.
|
|
8
|
+
4. Consilium / Peer Critique Round 2 (`peer_critique_enabled: boolean`).
|
|
9
|
+
5. Analytics filtering by preset and CSV/JSON export.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Technical Specifications
|
|
14
|
+
|
|
15
|
+
### 1. User Candidate Override
|
|
16
|
+
- **Schema**: `PresetSchema` field `allow_candidate_override: z.boolean().default(false)`.
|
|
17
|
+
- **Backend**:
|
|
18
|
+
- `writeCandidateWorkspace` stores candidates in `.moa-workspaces/<runId>/candidate-<index>`.
|
|
19
|
+
- Add API route `POST /dsh-moa/promote` with `{ runId, candidateIndex }` that promotes specified candidate files to the project root.
|
|
20
|
+
- In `formatMoAResponse`, if enabled, render interactive action hints:
|
|
21
|
+
`🔄 Alternative Candidate Override: /moa promote <runId> <candidateIndex>`
|
|
22
|
+
- **Client**: Add toggle in Section 1 / Section 3 of `lib/client.js`.
|
|
23
|
+
|
|
24
|
+
### 2. 10 Built-in Presets & Candidate Role Personas
|
|
25
|
+
- **Presets**:
|
|
26
|
+
1. `default`: General MoA ensemble.
|
|
27
|
+
2. `code-review`: Zero regressions, backward compatibility, edge cases.
|
|
28
|
+
3. `fast-audit`: Quorum mitigation, low latency.
|
|
29
|
+
4. `deep-architect`: System architecture, API contracts, curator synthesis.
|
|
30
|
+
5. `bug-hunter`: Concurrency, race conditions, memory leaks, null safety.
|
|
31
|
+
6. `refactor-cleanup`: Ponytail minimalism, stdlib first, zero external dependencies.
|
|
32
|
+
7. `frontend-ui`: Ergonomics, DSH design tokens, responsive layout, no AI slop.
|
|
33
|
+
8. `security-audit`: OWASP Top 10, input sanitization, safe error handling.
|
|
34
|
+
9. `math-logic`: Algorithmic correctness, asymptotic proofs.
|
|
35
|
+
10. `creative-brainstorm`: High divergence, alternative architectures.
|
|
36
|
+
- **Candidate Roles (`role_persona`)**:
|
|
37
|
+
- `minimalist`: Ponytail style, stdlib only, fewest lines.
|
|
38
|
+
- `robustness`: Paranoid validation, edge cases, defensive typing.
|
|
39
|
+
- `performance`: Memory efficiency, asymptotic complexity.
|
|
40
|
+
- `tester`: TDD, unit test coverage, mock isolation.
|
|
41
|
+
- `general`: Standard expert proposer.
|
|
42
|
+
|
|
43
|
+
### 3. Syntax Pre-check Gate with Judge Auto-fix Directive
|
|
44
|
+
- **Checker**:
|
|
45
|
+
- JS/MJS/CJS: `new vm.Script(code)` from built-in `node:vm` (instant, memory-safe).
|
|
46
|
+
- JSON: `JSON.parse(code)`.
|
|
47
|
+
- Python: `python3 -m py_compile`.
|
|
48
|
+
- **Judge Directive**:
|
|
49
|
+
- If a candidate has a syntax warning, inform the judge:
|
|
50
|
+
`CRITICAL JUDGE DIRECTIVE: If Candidate X has the best overall architecture or solution despite this syntax error, DO NOT reject them! You MUST correct the syntax error directly during final code synthesis and declare Candidate X the winner.`
|
|
51
|
+
|
|
52
|
+
### 4. Consilium / Peer Critique Round 2
|
|
53
|
+
- **Schema**: `peer_critique_enabled: z.boolean().default(false)`.
|
|
54
|
+
- **Pipeline**:
|
|
55
|
+
- If enabled and `candidates.length >= 2`, run Round 2:
|
|
56
|
+
- Pass anonymized candidate proposals to each proposer.
|
|
57
|
+
- Collect refined solutions with peer critique.
|
|
58
|
+
- Supply improved proposals to Judge/Curator.
|
|
59
|
+
|
|
60
|
+
### 5. Analytics Filtering & CSV/JSON Export
|
|
61
|
+
- **Leaderboard Filter**: `getMoaLeaderboard(historyFilePath, limit, filterPreset)`.
|
|
62
|
+
- **Export Endpoints**:
|
|
63
|
+
- `GET /dsh-moa/leaderboard?preset=<name>&format=json|csv`.
|
|
64
|
+
- **Client UI**:
|
|
65
|
+
- Dropdown filter by preset in Section 4.
|
|
66
|
+
- `Export CSV` / `Export JSON` action buttons.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Verification
|
|
71
|
+
- Unit tests for all 5 features in `test/moa-power-pack.test.mjs`.
|
|
72
|
+
- Total test suite passing with 0 regressions.
|
|
73
|
+
- Live deployment to `/home/vadim/.dsh/profiles/web` on MiniPC and endpoint verification.
|
package/lib/client.js
CHANGED
|
@@ -55,6 +55,19 @@ window.__ModuleLoader__.load({
|
|
|
55
55
|
'aggregator.curator_hint': 'When enabled: the curator highlights the finest components of each candidate, applies the antipatterns rubric, and advises which agent model should assemble the solution.',
|
|
56
56
|
'aggregator.stream_label': 'Live Stream Curator/Judge Thinking',
|
|
57
57
|
'aggregator.stream_hint': 'Streams aggregator tokens directly to chat in real time for zero-latency initial response.',
|
|
58
|
+
'aggregator.peer_label': 'Consilium: Peer Critique (Round 2)',
|
|
59
|
+
'aggregator.peer_hint': 'Candidates critique each other\'s solutions and refine their code before final judge synthesis.',
|
|
60
|
+
'aggregator.override_label': 'Allow Candidate Override Actions',
|
|
61
|
+
'aggregator.override_hint': 'Keeps candidate workspaces in .moa to let you apply any candidate files via chat command.',
|
|
62
|
+
'proposers.role_label': 'Persona:',
|
|
63
|
+
'proposers.role_general': 'General',
|
|
64
|
+
'proposers.role_minimalist': 'Minimalist',
|
|
65
|
+
'proposers.role_robustness': 'Robustness',
|
|
66
|
+
'proposers.role_performance': 'Performance',
|
|
67
|
+
'proposers.role_tester': 'Tester',
|
|
68
|
+
'leaderboard.filter_all': 'All Presets',
|
|
69
|
+
'leaderboard.export_csv': 'Export CSV',
|
|
70
|
+
'leaderboard.export_json': 'Export JSON',
|
|
58
71
|
'aggregator.blind_label': 'Blind Review (Anonymize Candidates for Judge)',
|
|
59
72
|
'aggregator.blind_hint': 'When enabled: candidate model names and providers are hidden from the judge prompt to prevent family/brand bias.',
|
|
60
73
|
'aggregator.timeout_label': 'Judge synthesis timeout (sec):',
|
|
@@ -102,6 +115,99 @@ window.__ModuleLoader__.load({
|
|
|
102
115
|
'slash.desc': 'Run turn with Mixture of Agents ensemble synthesis (/moa [preset] <prompt>)',
|
|
103
116
|
}
|
|
104
117
|
|
|
118
|
+
const zh = {
|
|
119
|
+
title: '智能体混合 (MoA)',
|
|
120
|
+
description: '多模型协同编排:通过 /moa <提示词> 进行候选模型并行提案与主裁判综合。',
|
|
121
|
+
'header.title': '智能体混合 (MoA) 编排系统',
|
|
122
|
+
'header.sub': '由多个候选提案模型并行生成方案,再由领先的聚合裁判模型进行终审评测与代码提拔。完成后自动切回您的会话模型。',
|
|
123
|
+
'badge.online': 'MoA 已启用',
|
|
124
|
+
'badge.offline': '主机不可达',
|
|
125
|
+
'badge.preset': '预设:{name}',
|
|
126
|
+
'badge.models': '{count} 个可用模型',
|
|
127
|
+
'badge.candidates': '{count} 个提案模型',
|
|
128
|
+
'presets.title': '📋 预设管理',
|
|
129
|
+
'presets.desc': '选择当前活动预设或针对特定任务(如代码审查、数学推演、快速分流)创建自定义配置。',
|
|
130
|
+
'presets.active_badge': '默认激活',
|
|
131
|
+
'presets.set_default': '设为默认',
|
|
132
|
+
'presets.new_btn': '+ 新建预设',
|
|
133
|
+
'presets.del_btn': '删除',
|
|
134
|
+
'presets.del_confirm': '确定要删除预设“{name}”吗?',
|
|
135
|
+
'presets.create': '创建',
|
|
136
|
+
'presets.cancel': '取消',
|
|
137
|
+
'presets.name_placeholder': '新预设名称(例如:fast-audit)',
|
|
138
|
+
'aggregator.title': '⚖️ 领先模型 / 裁判(综合与审计)',
|
|
139
|
+
'aggregator.desc': '对比候选方案,消除差异,过滤幻觉,并综合生成最终生产级解决方案。',
|
|
140
|
+
'aggregator.temp_label': '裁判采样温度 (Temperature)',
|
|
141
|
+
'aggregator.temp_hint': '推荐:0.2 – 0.4,以确保严格的验证和确定性的代码综合。',
|
|
142
|
+
'aggregator.criteria_label': '裁判评测标准(可选):',
|
|
143
|
+
'aggregator.criteria_placeholder': '例如:优先考虑高性能、零外部依赖、完善的边界异常处理...',
|
|
144
|
+
'aggregator.questions_label': '对于宽泛任务先提出澄清问题(需求问卷)',
|
|
145
|
+
'aggregator.questions_hint': '启用时:对于简短或模糊的任务,裁判在生成代码前会先综合一份包含 2–4 个问题的选项清单。',
|
|
146
|
+
'aggregator.curator_label': '策展人综合与主组装模型建议',
|
|
147
|
+
'aggregator.curator_hint': '启用时:策展人会提炼每个候选方案的精华部分,应用反模式清单,并建议由哪个智能体模型组装最终方案。',
|
|
148
|
+
'aggregator.stream_label': '实时流式传输裁判/策展人思考过程',
|
|
149
|
+
'aggregator.stream_hint': '将裁判生成的思考过程实时推送到聊天框,实现零延迟首字响应。',
|
|
150
|
+
'aggregator.peer_label': '决策评议会:候选模型交叉互评(第二轮 Consilium)',
|
|
151
|
+
'aggregator.peer_hint': '在主裁判终审前,各候选模型互相审阅并改进彼此的方案代码。',
|
|
152
|
+
'aggregator.override_label': '允许手动候选方案提拔操作 (Override)',
|
|
153
|
+
'aggregator.override_hint': '在 .moa 目录中保留候选模型的工作区,允许通过聊天命令提拔任意候选模型的文件。',
|
|
154
|
+
'proposers.role_label': '工程角色画像:',
|
|
155
|
+
'proposers.role_general': '通用平衡 (General)',
|
|
156
|
+
'proposers.role_minimalist': '极简标准库 (Minimalist)',
|
|
157
|
+
'proposers.role_robustness': '防御鲁棒 (Robustness)',
|
|
158
|
+
'proposers.role_performance': '极致性能 (Performance)',
|
|
159
|
+
'proposers.role_tester': '测试驱动 (Tester)',
|
|
160
|
+
'leaderboard.filter_all': '全部预设',
|
|
161
|
+
'leaderboard.export_csv': '导出 CSV',
|
|
162
|
+
'leaderboard.export_json': '导出 JSON',
|
|
163
|
+
'aggregator.blind_label': '盲审模式(对裁判隐藏候选模型名称)',
|
|
164
|
+
'aggregator.blind_hint': '启用时:从裁判提示词中匿名化候选模型的提供商和名称,杜绝品牌偏见。',
|
|
165
|
+
'aggregator.timeout_label': '裁判综合超时时长(秒):',
|
|
166
|
+
'proposers.timeout_label': '候选模型生成超时时长(秒):',
|
|
167
|
+
'leaderboard.title': '🏆 模型胜率排行榜',
|
|
168
|
+
'leaderboard.desc': '基于历史记录的 MoA 裁判评测判决数据统计指标。',
|
|
169
|
+
'leaderboard.model': '模型',
|
|
170
|
+
'leaderboard.runs': '运行次数',
|
|
171
|
+
'leaderboard.wins': '胜出次数',
|
|
172
|
+
'leaderboard.winrate': '胜率',
|
|
173
|
+
'leaderboard.cost': '平均成本',
|
|
174
|
+
'leaderboard.empty': '暂无历史运行记录。',
|
|
175
|
+
'aggregator.fallbacks_title': '备用裁判链',
|
|
176
|
+
'aggregator.fallbacks_desc': '当主裁判触发速率限制或服务不可用时,自动顺序调用的备用模型。',
|
|
177
|
+
'aggregator.add_fallback_btn': '+ 添加备用裁判',
|
|
178
|
+
'proposers.quorum_label': '法定人数快进(防拖尾模型机制)',
|
|
179
|
+
'proposers.quorum_hint': '当 67% 以上候选模型完成时,开启缓冲定时器(默认 10 秒),不再死等卡顿的模型。',
|
|
180
|
+
'proposers.grace_label': '缓冲等待时长(秒):',
|
|
181
|
+
'proposers.title': '👥 候选提案模型 (Proposers)',
|
|
182
|
+
'proposers.desc': '并行生成互相独立的解决方案,最大化思路多样性与解空间覆盖度。',
|
|
183
|
+
'proposers.parallel_count': '并行提案模型数量',
|
|
184
|
+
'proposers.candidate': '候选模型',
|
|
185
|
+
'proposers.add_btn': '+ 添加候选模型',
|
|
186
|
+
'proposers.temp_label': '候选模型采样温度 (Temperature)',
|
|
187
|
+
'proposers.temp_hint': '推荐:0.6 – 0.8,鼓励创造性、发散性的独立探索。',
|
|
188
|
+
'stats.title': '📈 MoA 数据分析与遥测',
|
|
189
|
+
'stats.desc': '会话遥测、历史运行追踪及模型胜率统计。',
|
|
190
|
+
'stats.total_runs': '总运行次数',
|
|
191
|
+
'stats.avg_cost': '平均每次成本',
|
|
192
|
+
'stats.active_models': '已配置模型数',
|
|
193
|
+
'stats.preset_count': '预设总数',
|
|
194
|
+
'picker.select': '选择模型…',
|
|
195
|
+
'picker.search_placeholder': '按名称或提供商搜索模型…',
|
|
196
|
+
'picker.found': '找到',
|
|
197
|
+
'picker.of': '共',
|
|
198
|
+
'picker.clear': '清除',
|
|
199
|
+
'picker.none': '未找到匹配的模型。',
|
|
200
|
+
'picker.custom': '使用自定义:',
|
|
201
|
+
'actions.save': '保存配置',
|
|
202
|
+
'actions.saving': '正在保存…',
|
|
203
|
+
'actions.saved': '配置已成功保存!',
|
|
204
|
+
'actions.retry': '重新加载配置',
|
|
205
|
+
'badge.disabled': 'MoA 已禁用',
|
|
206
|
+
'config.enabled_label': '启用 MoA(斜杠命令与轮次拦截路由)',
|
|
207
|
+
'slash.desc': '使用智能体混合 (MoA) 协同综合运行此轮次 (/moa [预设] <提示词>)',
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
|
|
105
211
|
function makeT(dict, fallback) {
|
|
106
212
|
return function t(key, vars) {
|
|
107
213
|
let val = (dict && dict[key]) || (fallback && fallback[key]) || key
|
|
@@ -461,6 +567,8 @@ window.__ModuleLoader__.load({
|
|
|
461
567
|
hostStatus,
|
|
462
568
|
stats,
|
|
463
569
|
leaderboard = [],
|
|
570
|
+
leaderboardPresetFilter = 'all',
|
|
571
|
+
setLeaderboardPresetFilter = () => {},
|
|
464
572
|
handleSave,
|
|
465
573
|
saveStatus,
|
|
466
574
|
reload,
|
|
@@ -877,6 +985,38 @@ window.__ModuleLoader__.load({
|
|
|
877
985
|
},
|
|
878
986
|
})
|
|
879
987
|
),
|
|
988
|
+
React.createElement(
|
|
989
|
+
'label',
|
|
990
|
+
{ style: { display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer', fontSize: 13, fontWeight: 500, marginTop: 8 } },
|
|
991
|
+
React.createElement('input', {
|
|
992
|
+
type: 'checkbox',
|
|
993
|
+
checked: Boolean(currentPreset.peer_critique_enabled),
|
|
994
|
+
onChange: (e) => updateCurrentPreset((p) => ({ ...p, peer_critique_enabled: e.target.checked })),
|
|
995
|
+
style: { accentColor: 'var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))', cursor: 'pointer' },
|
|
996
|
+
}),
|
|
997
|
+
t('aggregator.peer_label')
|
|
998
|
+
),
|
|
999
|
+
React.createElement(
|
|
1000
|
+
'div',
|
|
1001
|
+
{ style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)', marginLeft: 22 } },
|
|
1002
|
+
t('aggregator.peer_hint')
|
|
1003
|
+
),
|
|
1004
|
+
React.createElement(
|
|
1005
|
+
'label',
|
|
1006
|
+
{ style: { display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer', fontSize: 13, fontWeight: 500, marginTop: 8 } },
|
|
1007
|
+
React.createElement('input', {
|
|
1008
|
+
type: 'checkbox',
|
|
1009
|
+
checked: Boolean(currentPreset.allow_candidate_override),
|
|
1010
|
+
onChange: (e) => updateCurrentPreset((p) => ({ ...p, allow_candidate_override: e.target.checked })),
|
|
1011
|
+
style: { accentColor: 'var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))', cursor: 'pointer' },
|
|
1012
|
+
}),
|
|
1013
|
+
t('aggregator.override_label')
|
|
1014
|
+
),
|
|
1015
|
+
React.createElement(
|
|
1016
|
+
'div',
|
|
1017
|
+
{ style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)', marginLeft: 22 } },
|
|
1018
|
+
t('aggregator.override_hint')
|
|
1019
|
+
),
|
|
880
1020
|
/* Fallback Judges Chain */
|
|
881
1021
|
React.createElement(
|
|
882
1022
|
'div',
|
|
@@ -1052,6 +1192,28 @@ window.__ModuleLoader__.load({
|
|
|
1052
1192
|
t,
|
|
1053
1193
|
})
|
|
1054
1194
|
),
|
|
1195
|
+
React.createElement(
|
|
1196
|
+
'select',
|
|
1197
|
+
{
|
|
1198
|
+
className: 'moa-select',
|
|
1199
|
+
style: { width: 105, height: 32, fontSize: 12, padding: '0 6px' },
|
|
1200
|
+
value: ref.role_persona || 'general',
|
|
1201
|
+
title: t('proposers.role_label'),
|
|
1202
|
+
onChange: (e) => {
|
|
1203
|
+
const newRole = e.target.value
|
|
1204
|
+
updateCurrentPreset((p) => {
|
|
1205
|
+
const refs = [...(p.reference_models || [])]
|
|
1206
|
+
refs[idx] = { ...refs[idx], role_persona: newRole }
|
|
1207
|
+
return { ...p, reference_models: refs }
|
|
1208
|
+
})
|
|
1209
|
+
},
|
|
1210
|
+
},
|
|
1211
|
+
React.createElement('option', { value: 'general' }, t('proposers.role_general') || 'General'),
|
|
1212
|
+
React.createElement('option', { value: 'minimalist' }, t('proposers.role_minimalist') || 'Minimalist'),
|
|
1213
|
+
React.createElement('option', { value: 'robustness' }, t('proposers.role_robustness') || 'Robustness'),
|
|
1214
|
+
React.createElement('option', { value: 'performance' }, t('proposers.role_performance') || 'Performance'),
|
|
1215
|
+
React.createElement('option', { value: 'tester' }, t('proposers.role_tester') || 'Tester')
|
|
1216
|
+
),
|
|
1055
1217
|
currentRefs.length > 1 &&
|
|
1056
1218
|
React.createElement(
|
|
1057
1219
|
'button',
|
|
@@ -1139,7 +1301,46 @@ window.__ModuleLoader__.load({
|
|
|
1139
1301
|
Array.isArray(leaderboard) && leaderboard.length > 0 && React.createElement(
|
|
1140
1302
|
'div',
|
|
1141
1303
|
{ style: { marginTop: 12, display: 'flex', flexDirection: 'column', gap: 6 } },
|
|
1142
|
-
React.createElement(
|
|
1304
|
+
React.createElement(
|
|
1305
|
+
'div',
|
|
1306
|
+
{ style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8, flexWrap: 'wrap' } },
|
|
1307
|
+
React.createElement('div', { style: { fontSize: 13, fontWeight: 600, color: 'var(--dsw-alias-label-primary)' } }, t('leaderboard.title')),
|
|
1308
|
+
React.createElement(
|
|
1309
|
+
'div',
|
|
1310
|
+
{ style: { display: 'flex', alignItems: 'center', gap: 6 } },
|
|
1311
|
+
React.createElement(
|
|
1312
|
+
'select',
|
|
1313
|
+
{
|
|
1314
|
+
className: 'moa-select',
|
|
1315
|
+
style: { height: 26, fontSize: 11, padding: '0 6px' },
|
|
1316
|
+
value: leaderboardPresetFilter,
|
|
1317
|
+
onChange: (e) => setLeaderboardPresetFilter(e.target.value),
|
|
1318
|
+
},
|
|
1319
|
+
React.createElement('option', { value: 'all' }, t('leaderboard.filter_all')),
|
|
1320
|
+
presets.map((p) => React.createElement('option', { key: p.name, value: p.name }, p.name))
|
|
1321
|
+
),
|
|
1322
|
+
React.createElement(
|
|
1323
|
+
'a',
|
|
1324
|
+
{
|
|
1325
|
+
href: `/dsh-moa/history/export?format=csv${leaderboardPresetFilter !== 'all' ? '&preset=' + encodeURIComponent(leaderboardPresetFilter) : ''}`,
|
|
1326
|
+
download: 'moa-history.csv',
|
|
1327
|
+
className: 'moa-btn',
|
|
1328
|
+
style: { height: 26, fontSize: 11, padding: '0 8px', textDecoration: 'none', display: 'inline-flex', alignItems: 'center' },
|
|
1329
|
+
},
|
|
1330
|
+
t('leaderboard.export_csv') || 'CSV'
|
|
1331
|
+
),
|
|
1332
|
+
React.createElement(
|
|
1333
|
+
'a',
|
|
1334
|
+
{
|
|
1335
|
+
href: `/dsh-moa/history/export?format=json${leaderboardPresetFilter !== 'all' ? '&preset=' + encodeURIComponent(leaderboardPresetFilter) : ''}`,
|
|
1336
|
+
download: 'moa-history.json',
|
|
1337
|
+
className: 'moa-btn',
|
|
1338
|
+
style: { height: 26, fontSize: 11, padding: '0 8px', textDecoration: 'none', display: 'inline-flex', alignItems: 'center' },
|
|
1339
|
+
},
|
|
1340
|
+
t('leaderboard.export_json') || 'JSON'
|
|
1341
|
+
)
|
|
1342
|
+
)
|
|
1343
|
+
),
|
|
1143
1344
|
React.createElement('div', { style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)', marginTop: -4 } }, t('leaderboard.desc')),
|
|
1144
1345
|
React.createElement(
|
|
1145
1346
|
'div',
|
|
@@ -1257,6 +1458,7 @@ window.__ModuleLoader__.load({
|
|
|
1257
1458
|
const [hostStatus, setHostStatus] = React.useState('connecting')
|
|
1258
1459
|
const [stats, setStats] = React.useState({ totalRuns: null, avgCostUsd: null })
|
|
1259
1460
|
const [leaderboard, setLeaderboard] = React.useState([])
|
|
1461
|
+
const [leaderboardPresetFilter, setLeaderboardPresetFilter] = React.useState('all')
|
|
1260
1462
|
|
|
1261
1463
|
React.useEffect(() => {
|
|
1262
1464
|
if (!scope || !snapshot) return
|
|
@@ -1288,7 +1490,10 @@ window.__ModuleLoader__.load({
|
|
|
1288
1490
|
setHostStatus('offline')
|
|
1289
1491
|
})
|
|
1290
1492
|
|
|
1291
|
-
|
|
1493
|
+
const lbUrl = leaderboardPresetFilter !== 'all'
|
|
1494
|
+
? `/dsh-moa/leaderboard?preset=${encodeURIComponent(leaderboardPresetFilter)}`
|
|
1495
|
+
: '/dsh-moa/leaderboard'
|
|
1496
|
+
fetch(lbUrl, { cache: 'no-store' })
|
|
1292
1497
|
.then((r) => r.json())
|
|
1293
1498
|
.then((data) => {
|
|
1294
1499
|
if (data && data.ok && Array.isArray(data.models)) {
|
|
@@ -1406,6 +1611,8 @@ window.__ModuleLoader__.load({
|
|
|
1406
1611
|
hostStatus,
|
|
1407
1612
|
stats,
|
|
1408
1613
|
leaderboard,
|
|
1614
|
+
leaderboardPresetFilter,
|
|
1615
|
+
setLeaderboardPresetFilter,
|
|
1409
1616
|
handleSave,
|
|
1410
1617
|
saveStatus,
|
|
1411
1618
|
reload,
|
|
@@ -1519,7 +1726,7 @@ window.__ModuleLoader__.load({
|
|
|
1519
1726
|
|
|
1520
1727
|
if (typeof ctx.effect === 'function') {
|
|
1521
1728
|
ctx.effect(() => {
|
|
1522
|
-
const undo = [addLocale('en', en)]
|
|
1729
|
+
const undo = [addLocale('en', en), addLocale('zh', zh)]
|
|
1523
1730
|
return () => {
|
|
1524
1731
|
for (const off of undo) {
|
|
1525
1732
|
if (typeof off === 'function') {
|
package/lib/file-workspace.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import vm from 'node:vm'
|
|
1
2
|
import fs from 'node:fs/promises'
|
|
2
3
|
import path from 'node:path'
|
|
3
4
|
|
|
@@ -151,10 +152,10 @@ export function isRefinementTask(userPrompt = '', projectFiles = []) {
|
|
|
151
152
|
|
|
152
153
|
// Greenfield creation phrases - ALWAYS fresh task
|
|
153
154
|
const freshPhrases = [
|
|
154
|
-
'
|
|
155
|
-
'
|
|
155
|
+
'from scratch', 'new project', 'new app', 'create a new', 'build a new', 'generate a new',
|
|
156
|
+
'从头开始', '新建项目', '创建项目', '新应用', '新建应用',
|
|
157
|
+
'с нуля', 'новый проект', 'новое приложение', 'создай проект', 'создай приложение', 'создай игру', 'создай сервис',
|
|
156
158
|
'сделай проект', 'сделай приложение', 'сделай игру',
|
|
157
|
-
'create a new', 'build a new', 'generate a new',
|
|
158
159
|
]
|
|
159
160
|
if (freshPhrases.some((phrase) => p.includes(phrase))) {
|
|
160
161
|
return false
|
|
@@ -162,9 +163,11 @@ export function isRefinementTask(userPrompt = '', projectFiles = []) {
|
|
|
162
163
|
|
|
163
164
|
// Modification keywords
|
|
164
165
|
const modKeywords = [
|
|
165
|
-
'добавь', 'измени', 'поменяй', 'исправь', 'обнови', 'переделай', 'доработай', 'удали',
|
|
166
166
|
'add', 'change', 'update', 'fix', 'modify', 'refactor', 'improve', 'enhance', 'adjust',
|
|
167
|
-
'style', 'css', '
|
|
167
|
+
'style', 'css', 'color', 'theme', 'bug',
|
|
168
|
+
'添加', '修改', '更新', '修复', '重构', '改进', '调整', '样式', '颜色', '主题',
|
|
169
|
+
'добавь', 'измени', 'поменяй', 'исправь', 'обнови', 'переделай', 'доработай', 'удали',
|
|
170
|
+
'темн', 'светл', 'цвет', 'кнопк', 'баг',
|
|
168
171
|
]
|
|
169
172
|
|
|
170
173
|
return modKeywords.some((kw) => p.includes(kw))
|
|
@@ -275,3 +278,48 @@ async function getFilesRecursively(dir) {
|
|
|
275
278
|
await scan(dir)
|
|
276
279
|
return results
|
|
277
280
|
}
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Verifies the syntactic validity of generated code files.
|
|
285
|
+
* Uses Node.js in-memory vm.Script for JS (with ES module export/import transformation) and JSON.parse for JSON.
|
|
286
|
+
*/
|
|
287
|
+
export function verifyFileSyntax(files = []) {
|
|
288
|
+
if (!Array.isArray(files)) return []
|
|
289
|
+
const warnings = []
|
|
290
|
+
|
|
291
|
+
for (const file of files) {
|
|
292
|
+
if (!file || typeof file.content !== 'string' || !file.relativePath) continue
|
|
293
|
+
const ext = path.extname(file.relativePath).toLowerCase()
|
|
294
|
+
|
|
295
|
+
if (ext === '.js' || ext === '.mjs' || ext === '.cjs') {
|
|
296
|
+
try {
|
|
297
|
+
const transformed = file.content
|
|
298
|
+
.replace(/^([ \t]*)export\s+default\s+/gm, '$1')
|
|
299
|
+
.replace(/^([ \t]*)export\s*\{[^}]*\}\s*;?/gm, '$1')
|
|
300
|
+
.replace(/^([ \t]*)export\s+(const|let|var|function|class|async\s+function)\s+/gm, '$1$2 ')
|
|
301
|
+
.replace(/^([ \t]*)import\s+[^;\n]+;?/gm, '$1')
|
|
302
|
+
.replace(/^([ \t]*)export\s+\*\s+from\s+[^;\n]+;?/gm, '$1')
|
|
303
|
+
new vm.Script(transformed)
|
|
304
|
+
} catch (err) {
|
|
305
|
+
warnings.push({
|
|
306
|
+
file: file.relativePath,
|
|
307
|
+
error: err?.message || String(err),
|
|
308
|
+
line: err?.lineNumber || 1,
|
|
309
|
+
})
|
|
310
|
+
}
|
|
311
|
+
} else if (ext === '.json') {
|
|
312
|
+
try {
|
|
313
|
+
JSON.parse(file.content)
|
|
314
|
+
} catch (err) {
|
|
315
|
+
warnings.push({
|
|
316
|
+
file: file.relativePath,
|
|
317
|
+
error: err?.message || String(err),
|
|
318
|
+
line: 1,
|
|
319
|
+
})
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return warnings
|
|
325
|
+
}
|
package/lib/history.js
CHANGED
|
@@ -159,7 +159,7 @@ export function getMoaHistory(limit = 20, offset = 0, filePath = DEFAULT_HISTORY
|
|
|
159
159
|
/**
|
|
160
160
|
* Calculates win-rate leaderboard and model performance statistics across history.
|
|
161
161
|
*/
|
|
162
|
-
export function getMoaLeaderboard(filePath = DEFAULT_HISTORY_FILE) {
|
|
162
|
+
export function getMoaLeaderboard(filePath = DEFAULT_HISTORY_FILE, options = {}) {
|
|
163
163
|
try {
|
|
164
164
|
if (!fs.existsSync(filePath)) {
|
|
165
165
|
return { totalRuns: 0, models: [] }
|
|
@@ -170,11 +170,15 @@ export function getMoaLeaderboard(filePath = DEFAULT_HISTORY_FILE) {
|
|
|
170
170
|
const totalRuns = lines.length
|
|
171
171
|
|
|
172
172
|
const modelStats = new Map()
|
|
173
|
+
const presetFilter = typeof options === 'string' ? options : (options?.presetFilter || options?.preset || null)
|
|
173
174
|
|
|
175
|
+
let filteredRuns = 0
|
|
174
176
|
for (const line of lines) {
|
|
175
177
|
try {
|
|
176
178
|
const item = JSON.parse(line)
|
|
177
179
|
if (!item) continue
|
|
180
|
+
if (presetFilter && presetFilter !== 'all' && item.preset !== presetFilter) continue
|
|
181
|
+
filteredRuns++
|
|
178
182
|
|
|
179
183
|
const winner = item.winnerModel ? String(item.winnerModel).trim() : null
|
|
180
184
|
|
|
@@ -218,7 +222,7 @@ export function getMoaLeaderboard(filePath = DEFAULT_HISTORY_FILE) {
|
|
|
218
222
|
}))
|
|
219
223
|
.sort((a, b) => b.wins - a.wins || b.winRate - a.winRate)
|
|
220
224
|
|
|
221
|
-
return { totalRuns, models }
|
|
225
|
+
return { totalRuns: presetFilter && presetFilter !== 'all' ? filteredRuns : totalRuns, models, preset: presetFilter }
|
|
222
226
|
} catch (err) {
|
|
223
227
|
console.warn('[dsh-moa] Failed to calculate leaderboard:', err)
|
|
224
228
|
return { totalRuns: 0, models: [] }
|
|
@@ -244,3 +248,53 @@ export function getMoaRunById(runId, filePath = DEFAULT_HISTORY_FILE) {
|
|
|
244
248
|
return null
|
|
245
249
|
}
|
|
246
250
|
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Exports history data in JSON or CSV format with optional preset filtering.
|
|
254
|
+
*/
|
|
255
|
+
export function exportMoaHistory(filePath = DEFAULT_HISTORY_FILE, options = {}) {
|
|
256
|
+
try {
|
|
257
|
+
if (!fs.existsSync(filePath)) {
|
|
258
|
+
return options?.format === 'csv' ? 'id,timestamp,preset,winnerModel,totalTokens,totalCostUsd,durationMs\n' : '[]'
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const raw = fs.readFileSync(filePath, 'utf8')
|
|
262
|
+
const lines = raw.trim().split('\n').filter(Boolean)
|
|
263
|
+
const presetFilter = typeof options === 'string' ? options : (options?.presetFilter || options?.preset || null)
|
|
264
|
+
const format = options?.format === 'csv' ? 'csv' : 'json'
|
|
265
|
+
|
|
266
|
+
const records = []
|
|
267
|
+
for (const line of lines) {
|
|
268
|
+
try {
|
|
269
|
+
const item = JSON.parse(line)
|
|
270
|
+
if (!item) continue
|
|
271
|
+
if (presetFilter && presetFilter !== 'all' && item.preset !== presetFilter) continue
|
|
272
|
+
records.push(item)
|
|
273
|
+
} catch {}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (format === 'csv') {
|
|
277
|
+
const header = 'id,timestamp,preset,winnerModel,totalTokens,totalCostUsd,durationMs,isRefinement,isFastMode'
|
|
278
|
+
const rows = records.map((r) => {
|
|
279
|
+
const escapeCsv = (val) => `"${String(val ?? '').replace(/"/g, '""')}"`
|
|
280
|
+
return [
|
|
281
|
+
escapeCsv(r.id),
|
|
282
|
+
escapeCsv(r.timestamp),
|
|
283
|
+
escapeCsv(r.preset),
|
|
284
|
+
escapeCsv(r.winnerModel),
|
|
285
|
+
Number(r.totalTokens || 0),
|
|
286
|
+
Number(r.totalCostUsd || 0).toFixed(4),
|
|
287
|
+
Number(r.durationMs || 0),
|
|
288
|
+
Boolean(r.isRefinement),
|
|
289
|
+
Boolean(r.isFastMode),
|
|
290
|
+
].join(',')
|
|
291
|
+
})
|
|
292
|
+
return [header, ...rows].join('\n')
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return JSON.stringify(records, null, 2)
|
|
296
|
+
} catch (err) {
|
|
297
|
+
console.warn('[dsh-moa] Failed to export history:', err)
|
|
298
|
+
return options?.format === 'csv' ? '' : '[]'
|
|
299
|
+
}
|
|
300
|
+
}
|