@sebastientang/llm-council 0.2.0
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/LICENSE +21 -0
- package/README.md +286 -0
- package/bin/llm-council.js +2 -0
- package/dist/cli.js +1151 -0
- package/dist/index.cjs +986 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +393 -0
- package/dist/index.d.ts +393 -0
- package/dist/index.js +931 -0
- package/dist/index.js.map +1 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sebastien Tang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# LLM Council
|
|
2
|
+
|
|
3
|
+
When a decision is too important for one model's opinion, convene a council.
|
|
4
|
+
|
|
5
|
+
LLM Council is a TypeScript library for structured multi-model deliberation. It orchestrates multiple LLM participants through debate rounds, then synthesizes their arguments into a single recommendation with confidence scores, risks, and validation gates.
|
|
6
|
+
|
|
7
|
+
## The Problem
|
|
8
|
+
|
|
9
|
+
Single-model outputs have blind spots. Asking the same model twice gives you correlated errors, not independent verification. Multi-model voting is better, but loses the reasoning behind each vote.
|
|
10
|
+
|
|
11
|
+
LLM Council fixes this by running structured deliberation protocols where participants build cases, attack assumptions, advocate for alternatives, and narrate failure scenarios — then synthesizes the full debate into a calibrated recommendation.
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @sebastientang/llm-council
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Library
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import {
|
|
23
|
+
Council,
|
|
24
|
+
AnthropicProvider,
|
|
25
|
+
AdversarialProtocol,
|
|
26
|
+
DialecticalSynthesizer,
|
|
27
|
+
PERSONAS,
|
|
28
|
+
} from '@sebastientang/llm-council'
|
|
29
|
+
|
|
30
|
+
const provider = new AnthropicProvider({
|
|
31
|
+
apiKey: process.env.ANTHROPIC_API_KEY!,
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const council = new Council({
|
|
35
|
+
providers: new Map([['anthropic', provider]]),
|
|
36
|
+
protocol: new AdversarialProtocol(),
|
|
37
|
+
synthesizer: new DialecticalSynthesizer(),
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const result = await council.deliberate({
|
|
41
|
+
topic: 'Should we build in-house or buy Salesforce?',
|
|
42
|
+
options: ['Build in-house CRM', 'Buy Salesforce'],
|
|
43
|
+
preferredOption: 'Build in-house CRM',
|
|
44
|
+
context: 'Team of 3 engineers. $50K annual budget. Need CRM in 3 months.',
|
|
45
|
+
participants: [
|
|
46
|
+
{ ...PERSONAS.proposer, provider: 'anthropic', model: 'claude-sonnet-4-20250514' },
|
|
47
|
+
{ ...PERSONAS.challenger, provider: 'anthropic', model: 'claude-sonnet-4-20250514' },
|
|
48
|
+
{ ...PERSONAS.steelmanner, provider: 'anthropic', model: 'claude-sonnet-4-20250514' },
|
|
49
|
+
{ ...PERSONAS.preMortem, provider: 'anthropic', model: 'claude-sonnet-4-20250514' },
|
|
50
|
+
],
|
|
51
|
+
rounds: 2,
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
console.log(result.synthesis.recommendation)
|
|
55
|
+
// "Buy Salesforce — the 3-month timeline and 3-person team make in-house build high-risk."
|
|
56
|
+
|
|
57
|
+
console.log(result.synthesis.confidence)
|
|
58
|
+
// 78
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### CLI
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# With Anthropic
|
|
65
|
+
ANTHROPIC_API_KEY=sk-... npx llm-council "Should we adopt microservices?"
|
|
66
|
+
|
|
67
|
+
# With OpenRouter (access to GPT-4o, Llama, Mixtral, etc.)
|
|
68
|
+
OPENROUTER_API_KEY=sk-... npx llm-council --provider openrouter "Should we adopt microservices?"
|
|
69
|
+
|
|
70
|
+
# With specific model and protocol
|
|
71
|
+
npx llm-council --provider openrouter --model openai/gpt-4o --protocol peer-review "topic"
|
|
72
|
+
|
|
73
|
+
# Full options
|
|
74
|
+
npx llm-council --help
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Protocols
|
|
78
|
+
|
|
79
|
+
### Adversarial Protocol (default)
|
|
80
|
+
|
|
81
|
+
Inspired by Dialectical Inquiry (Mason 1969) and Pre-Mortem analysis (Klein 2007):
|
|
82
|
+
|
|
83
|
+
1. **Round 1 — Independent Briefs**: Each participant writes their position without seeing others
|
|
84
|
+
- **Proposer**: Builds the strongest case for the preferred option
|
|
85
|
+
- **Challenger**: Red-teams the preferred option, finds vulnerabilities
|
|
86
|
+
- **Steelmanner**: Advocates for the rejected option at full strength
|
|
87
|
+
- **Pre-Mortem**: Assumes the preferred option failed, narrates how
|
|
88
|
+
|
|
89
|
+
2. **Round 2 — Targeted Rebuttals**: Each participant reads others' briefs and responds
|
|
90
|
+
|
|
91
|
+
3. **Synthesis**: An LLM reviews all briefs and rebuttals, producing a recommendation with confidence score, risks, dissent, validation gates, and assumptions.
|
|
92
|
+
|
|
93
|
+
### Peer-Review Protocol
|
|
94
|
+
|
|
95
|
+
Karpathy-style anonymized ranking:
|
|
96
|
+
|
|
97
|
+
1. **Round 1 — Independent Briefs**: Same as adversarial
|
|
98
|
+
2. **Round 2 — Anonymized Ranking**: Each participant sees all briefs labeled A, B, C, D (including their own) and ranks them with justifications
|
|
99
|
+
3. **Round 3 — Re-vote (optional)**: After seeing others' rankings, participants submit a final ranking
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
import { PeerReviewProtocol } from '@sebastientang/llm-council'
|
|
103
|
+
|
|
104
|
+
const council = new Council({
|
|
105
|
+
providers: new Map([['anthropic', provider]]),
|
|
106
|
+
protocol: new PeerReviewProtocol({ enableRevote: true }),
|
|
107
|
+
synthesizer: new ChairmanSynthesizer(),
|
|
108
|
+
})
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Synthesizers
|
|
112
|
+
|
|
113
|
+
### Dialectical Synthesizer (default)
|
|
114
|
+
|
|
115
|
+
Merges all arguments into a new recommendation. Weighs evidence over opinion, favors reversibility when confidence is low, synthesizes rather than averages.
|
|
116
|
+
|
|
117
|
+
### Chairman Synthesizer
|
|
118
|
+
|
|
119
|
+
Selects the best argument rather than creating a new synthesis. Evaluates responses on evidence quality, risk awareness, actionability, and logical coherence.
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
import { ChairmanSynthesizer } from '@sebastientang/llm-council'
|
|
123
|
+
|
|
124
|
+
const council = new Council({
|
|
125
|
+
providers: new Map([['anthropic', provider]]),
|
|
126
|
+
protocol: new AdversarialProtocol(),
|
|
127
|
+
synthesizer: new ChairmanSynthesizer({ temperature: 0.2 }),
|
|
128
|
+
})
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Providers
|
|
132
|
+
|
|
133
|
+
### AnthropicProvider
|
|
134
|
+
|
|
135
|
+
Direct access to Claude models via the Anthropic API.
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
const provider = new AnthropicProvider({
|
|
139
|
+
apiKey: process.env.ANTHROPIC_API_KEY!,
|
|
140
|
+
defaultModel: 'claude-sonnet-4-20250514', // optional
|
|
141
|
+
defaultMaxTokens: 1024, // optional
|
|
142
|
+
})
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### OpenRouterProvider
|
|
146
|
+
|
|
147
|
+
Access to 100+ models (GPT-4o, Llama, Mixtral, Gemini, etc.) through OpenRouter.
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { OpenRouterProvider } from '@sebastientang/llm-council'
|
|
151
|
+
|
|
152
|
+
const provider = new OpenRouterProvider({
|
|
153
|
+
apiKey: process.env.OPENROUTER_API_KEY!,
|
|
154
|
+
defaultModel: 'openai/gpt-4o', // optional
|
|
155
|
+
appName: 'my-app', // optional, shown in OpenRouter dashboard
|
|
156
|
+
})
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Architecture
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
Council
|
|
163
|
+
├── Provider (LLM API) → AnthropicProvider, OpenRouterProvider
|
|
164
|
+
├── Protocol (rounds) → AdversarialProtocol, PeerReviewProtocol
|
|
165
|
+
└── Synthesizer (final call) → DialecticalSynthesizer, ChairmanSynthesizer
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Three extension points — implement the interface and plug in:
|
|
169
|
+
|
|
170
|
+
| Component | Interface | Built-in |
|
|
171
|
+
|-----------|-----------|----------|
|
|
172
|
+
| **Provider** | `LLMProvider` | `AnthropicProvider`, `OpenRouterProvider` |
|
|
173
|
+
| **Protocol** | `Protocol` | `AdversarialProtocol`, `PeerReviewProtocol` |
|
|
174
|
+
| **Synthesizer** | `Synthesizer` | `DialecticalSynthesizer`, `ChairmanSynthesizer` |
|
|
175
|
+
|
|
176
|
+
## Events
|
|
177
|
+
|
|
178
|
+
Track progress during deliberation:
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
council.on('round:start', ({ round, participantCount }) => {
|
|
182
|
+
console.log(`Round ${round} starting with ${participantCount} participants`)
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
council.on('response', (message) => {
|
|
186
|
+
console.log(`${message.participantName} responded (${message.tokenCount.output} tokens)`)
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
council.on('synthesis:start', () => console.log('Synthesizing...'))
|
|
190
|
+
council.on('complete', (result) => console.log(`Done in ${result.metadata.durationMs}ms`))
|
|
191
|
+
council.on('error', (err) => console.error(err))
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Custom Personas
|
|
195
|
+
|
|
196
|
+
Use the built-in presets or define your own:
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
import { PERSONAS } from '@sebastientang/llm-council'
|
|
200
|
+
|
|
201
|
+
// Built-in: proposer, challenger, steelmanner, preMortem
|
|
202
|
+
const participant = {
|
|
203
|
+
...PERSONAS.proposer,
|
|
204
|
+
provider: 'anthropic',
|
|
205
|
+
model: 'claude-sonnet-4-20250514',
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Custom persona
|
|
209
|
+
const financialAnalyst = {
|
|
210
|
+
id: 'financial-analyst',
|
|
211
|
+
name: 'Financial Analyst',
|
|
212
|
+
provider: 'anthropic',
|
|
213
|
+
model: 'claude-sonnet-4-20250514',
|
|
214
|
+
systemPrompt: 'You are a financial analyst. Evaluate decisions through ROI, cash flow, and opportunity cost. Always quantify.',
|
|
215
|
+
temperature: 0.5,
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Token Budget
|
|
220
|
+
|
|
221
|
+
Control costs with per-response and total token limits:
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
const result = await council.deliberate({
|
|
225
|
+
// ...
|
|
226
|
+
tokenBudget: {
|
|
227
|
+
perResponse: 512, // max tokens per participant response
|
|
228
|
+
total: 8000, // tracked in metadata (not enforced)
|
|
229
|
+
},
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
console.log(result.metadata.totalTokens)
|
|
233
|
+
// { input: 4200, output: 2800 }
|
|
234
|
+
|
|
235
|
+
console.log(result.metadata.modelBreakdown)
|
|
236
|
+
// { "anthropic/claude-sonnet-4-20250514": { input: 4200, output: 2800 } }
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## API Reference
|
|
240
|
+
|
|
241
|
+
### `Council`
|
|
242
|
+
|
|
243
|
+
| Method | Description |
|
|
244
|
+
|--------|-------------|
|
|
245
|
+
| `deliberate(config)` | Run a full deliberation, returns `DeliberationResult` |
|
|
246
|
+
| `on(event, handler)` | Subscribe to events |
|
|
247
|
+
| `off(event, handler)` | Unsubscribe from events |
|
|
248
|
+
|
|
249
|
+
### `DeliberationConfig`
|
|
250
|
+
|
|
251
|
+
| Field | Type | Required | Description |
|
|
252
|
+
|-------|------|----------|-------------|
|
|
253
|
+
| `topic` | `string` | Yes | The decision or question |
|
|
254
|
+
| `options` | `string[]` | No | Explicit options to evaluate |
|
|
255
|
+
| `preferredOption` | `string` | No | Which way you're leaning |
|
|
256
|
+
| `context` | `string` | No | Background, constraints |
|
|
257
|
+
| `participants` | `Participant[]` | Yes | Min 2 participants |
|
|
258
|
+
| `rounds` | `number` | No | 1-5, default 2 |
|
|
259
|
+
| `tokenBudget` | `object` | No | Cost controls |
|
|
260
|
+
|
|
261
|
+
### `Synthesis`
|
|
262
|
+
|
|
263
|
+
| Field | Type | Description |
|
|
264
|
+
|-------|------|-------------|
|
|
265
|
+
| `recommendation` | `string` | The synthesized recommendation |
|
|
266
|
+
| `confidence` | `number` | 0-100 confidence score |
|
|
267
|
+
| `reasoning` | `string` | Why this recommendation |
|
|
268
|
+
| `risks` | `string[]` | Top risks to monitor |
|
|
269
|
+
| `dissent` | `string[]` | Counter-arguments that survived |
|
|
270
|
+
| `validationGates` | `string[]` | Measurable checkpoints |
|
|
271
|
+
| `assumptions` | `string[]` | What must hold true |
|
|
272
|
+
| `raw` | `string` | Raw synthesis output |
|
|
273
|
+
|
|
274
|
+
## Roadmap
|
|
275
|
+
|
|
276
|
+
- **v0.3**: Streaming responses, session persistence, web UI
|
|
277
|
+
|
|
278
|
+
## Inspired By
|
|
279
|
+
|
|
280
|
+
- [karpathy/llm-council](https://github.com/karpathy/llm-council) — Multi-model deliberation concept
|
|
281
|
+
- Dialectical Inquiry (Mason 1969) — Thesis tested by antithesis
|
|
282
|
+
- Pre-Mortem (Klein 2007) — Assume failure, backcast to causes
|
|
283
|
+
|
|
284
|
+
## License
|
|
285
|
+
|
|
286
|
+
MIT
|