forumkit 0.1.0__tar.gz

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 (35) hide show
  1. forumkit-0.1.0/.github/workflows/ci.yml +39 -0
  2. forumkit-0.1.0/.github/workflows/publish.yml +19 -0
  3. forumkit-0.1.0/.gitignore +54 -0
  4. forumkit-0.1.0/CHANGELOG.md +26 -0
  5. forumkit-0.1.0/CONTRIBUTING.md +77 -0
  6. forumkit-0.1.0/LICENSE +21 -0
  7. forumkit-0.1.0/PKG-INFO +228 -0
  8. forumkit-0.1.0/README.md +180 -0
  9. forumkit-0.1.0/SECURITY.md +143 -0
  10. forumkit-0.1.0/docs/API.md +319 -0
  11. forumkit-0.1.0/docs/ARCHITECTURE.md +149 -0
  12. forumkit-0.1.0/docs/CONCEPTS.md +119 -0
  13. forumkit-0.1.0/docs/examples/simple_debate.py +108 -0
  14. forumkit-0.1.0/forumkit/__init__.py +53 -0
  15. forumkit-0.1.0/forumkit/base.py +312 -0
  16. forumkit-0.1.0/forumkit/forum_base.py +346 -0
  17. forumkit-0.1.0/forumkit/memory/__init__.py +8 -0
  18. forumkit-0.1.0/forumkit/memory/episodic.py +121 -0
  19. forumkit-0.1.0/forumkit/memory/manager.py +42 -0
  20. forumkit-0.1.0/forumkit/memory/semantic.py +125 -0
  21. forumkit-0.1.0/forumkit/memory/working.py +99 -0
  22. forumkit-0.1.0/forumkit/orchestration/__init__.py +6 -0
  23. forumkit-0.1.0/forumkit/orchestration/decision.py +77 -0
  24. forumkit-0.1.0/forumkit/orchestration/execution.py +79 -0
  25. forumkit-0.1.0/forumkit/registry.py +80 -0
  26. forumkit-0.1.0/forumkit/security/__init__.py +5 -0
  27. forumkit-0.1.0/forumkit/security/prompt_sanitizer.py +66 -0
  28. forumkit-0.1.0/forumkit/telemetry.py +75 -0
  29. forumkit-0.1.0/pyproject.toml +86 -0
  30. forumkit-0.1.0/tests/integration/test_debate_round.py +80 -0
  31. forumkit-0.1.0/tests/unit/test_base.py +265 -0
  32. forumkit-0.1.0/tests/unit/test_forum.py +256 -0
  33. forumkit-0.1.0/tests/unit/test_memory.py +205 -0
  34. forumkit-0.1.0/tests/unit/test_orchestration.py +147 -0
  35. forumkit-0.1.0/tests/unit/test_security.py +252 -0
@@ -0,0 +1,39 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - name: Set up Python
16
+ uses: actions/setup-python@v5
17
+ with:
18
+ python-version: "3.11"
19
+
20
+ - name: Install dependencies
21
+ run: |
22
+ pip install -e ".[dev]"
23
+
24
+ - name: Type check
25
+ run: mypy forumkit/ --strict
26
+
27
+ - name: Lint
28
+ run: ruff check forumkit/ tests/
29
+
30
+ - name: Run unit tests
31
+ run: pytest tests/unit/ -v --tb=short
32
+
33
+ - name: Run integration tests
34
+ run: pytest tests/integration/ -v --tb=short -m integration --maxfail=1
35
+ continue-on-error: true
36
+ env:
37
+ REDIS_URL: redis://localhost:6379
38
+ PG_DSN: postgresql://postgres:postgres@localhost:5432/test
39
+ QDRANT_URL: http://localhost:6333
@@ -0,0 +1,19 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [created]
6
+
7
+ jobs:
8
+ deploy:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+ - uses: actions/setup-python@v5
13
+ with:
14
+ python-version: '3.11'
15
+ - run: pip install build
16
+ - run: python -m build
17
+ - uses: pypa/gh-action-pypi-publish@release/v1
18
+ with:
19
+ password: ${{ secrets.PYPI_API_TOKEN }}
@@ -0,0 +1,54 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Testing
24
+ .pytest_cache/
25
+ .coverage
26
+ htmlcov/
27
+
28
+ # Type checking
29
+ .mypy_cache/
30
+ .dmypy.json
31
+ dmypy.json
32
+
33
+ # IDEs
34
+ .vscode/
35
+ .idea/
36
+ *.swp
37
+ *.swo
38
+ *~
39
+
40
+ # Environment
41
+ .env
42
+ .venv
43
+ env/
44
+ venv/
45
+
46
+ # OS
47
+ .DS_Store
48
+ .AppleDouble
49
+ .LSOverride
50
+ *.log
51
+
52
+ # Project-specific
53
+ .venv/
54
+ dist/
@@ -0,0 +1,26 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
6
+
7
+ ## [0.1.0] - 2026-04-02
8
+
9
+ ### Added
10
+ - Structured debate protocol: fan-out → challenge → rebuttal → consensus scoring → synthesize
11
+ - Consensus scoring with dissent tracking and unanimity anomaly detection
12
+ - Role-gated agent hierarchy: HEAD, LEAD, SENIOR, JUNIOR roles with LLM-calling restrictions
13
+ - Memory backends: Redis (working memory), PostgreSQL (episodic), Qdrant (semantic)
14
+ - Async orchestration with LiteLLM tool-calling wrapper
15
+ - Prompt injection defense: NFKC normalization + BiDi character stripping + pattern redaction
16
+ - 59 unit tests with full mypy --strict compliance
17
+ - Comprehensive API documentation and examples
18
+ - GitHub Actions CI/CD workflow (pytest + mypy + ruff)
19
+
20
+ ### Documentation
21
+ - README with 5-minute quickstart
22
+ - API reference documenting all public types
23
+ - Concepts guide explaining the debate protocol
24
+ - Working example: simple_debate.py
25
+
26
+ [0.1.0]: https://github.com/vinitpai/forumkit/releases/tag/v0.1.0
@@ -0,0 +1,77 @@
1
+ # Contributing to forumkit
2
+
3
+ We welcome issues, discussions, and pull requests. This project is in alpha — your feedback shapes the direction.
4
+
5
+ ## Code of Conduct
6
+
7
+ Be respectful, constructive, and assume good intent.
8
+
9
+ ## Reporting Issues
10
+
11
+ - Use GitHub Issues for bugs, feature requests, documentation gaps
12
+ - Include Python version, OS, relevant error trace
13
+ - For security issues: email vinitpai@anthropic.com instead
14
+
15
+ ## Pull Requests
16
+
17
+ 1. Fork the repo
18
+ 2. Create a branch: `git checkout -b feature/my-feature`
19
+ 3. Write code following Google style guide (see [CLAUDE.md](CLAUDE.md))
20
+ 4. Add/update tests (pytest required)
21
+ 5. Run `ruff check --fix .` and `mypy . --strict`
22
+ 6. Commit with clear message (see below)
23
+ 7. Push and open PR with description of why (not just what)
24
+
25
+ ### Commit Message Format
26
+
27
+ ```
28
+ <type>: <subject>
29
+
30
+ <body (optional, explain why)>
31
+
32
+ <footer (optional)>
33
+ ```
34
+
35
+ Types: `feat`, `fix`, `refactor`, `test`, `docs`, `chore`
36
+
37
+ Example:
38
+ ```
39
+ feat: add timeout parameter to execute()
40
+
41
+ Previously, execute() had no way to enforce time limits. This adds
42
+ a configurable timeout_s parameter with exponential backoff on retry.
43
+
44
+ Fixes #123
45
+ ```
46
+
47
+ ## Testing
48
+
49
+ All new code requires tests:
50
+
51
+ ```bash
52
+ # Run tests
53
+ pytest tests/unit/ -v
54
+
55
+ # Run with coverage
56
+ pytest --cov=forumkit tests/unit/
57
+
58
+ # Type check
59
+ mypy forumkit/ --strict
60
+
61
+ # Lint
62
+ ruff check forumkit/
63
+ ```
64
+
65
+ ## Before Open Source
66
+
67
+ This project is currently private beta. Before we open source (v1.0), we need:
68
+
69
+ - [ ] Minimum 80% test coverage
70
+ - [ ] Zero mypy strict errors
71
+ - [ ] API documentation complete
72
+ - [ ] 2-3 working examples
73
+ - [ ] Security audit
74
+
75
+ ---
76
+
77
+ Questions? Open a discussion or email the maintainer.
forumkit-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vinit Pai
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.
@@ -0,0 +1,228 @@
1
+ Metadata-Version: 2.4
2
+ Name: forumkit
3
+ Version: 0.1.0
4
+ Summary: Multi-agent debate framework with structured consensus scoring and dissent tracking
5
+ Project-URL: Homepage, https://github.com/vinitpai/forumkit
6
+ Project-URL: Repository, https://github.com/vinitpai/forumkit
7
+ Project-URL: Issues, https://github.com/vinitpai/forumkit/issues
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: ai-agents,consensus,debate,llm,multi-agent
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
17
+ Requires-Python: >=3.11
18
+ Requires-Dist: aioredis>=2.0
19
+ Requires-Dist: asyncpg>=0.29
20
+ Requires-Dist: httpx>=0.27
21
+ Requires-Dist: litellm>=1.40
22
+ Requires-Dist: pydantic>=2.0
23
+ Requires-Dist: structlog>=24.0
24
+ Provides-Extra: all
25
+ Requires-Dist: aioredis>=2.0; extra == 'all'
26
+ Requires-Dist: asyncpg>=0.29; extra == 'all'
27
+ Requires-Dist: httpx>=0.27; extra == 'all'
28
+ Requires-Dist: kafka-python>=2.0; extra == 'all'
29
+ Requires-Dist: minio>=7.0; extra == 'all'
30
+ Requires-Dist: neo4j>=5.0; extra == 'all'
31
+ Provides-Extra: dev
32
+ Requires-Dist: coverage>=7.0; extra == 'dev'
33
+ Requires-Dist: mypy>=1.9; extra == 'dev'
34
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
35
+ Requires-Dist: pytest-mock>=3.12; extra == 'dev'
36
+ Requires-Dist: pytest>=8.0; extra == 'dev'
37
+ Requires-Dist: ruff>=0.4; extra == 'dev'
38
+ Provides-Extra: jepa
39
+ Requires-Dist: minio>=7.0; extra == 'jepa'
40
+ Requires-Dist: neo4j>=5.0; extra == 'jepa'
41
+ Provides-Extra: kafka
42
+ Requires-Dist: kafka-python>=2.0; extra == 'kafka'
43
+ Provides-Extra: memory
44
+ Requires-Dist: aioredis>=2.0; extra == 'memory'
45
+ Requires-Dist: asyncpg>=0.29; extra == 'memory'
46
+ Requires-Dist: httpx>=0.27; extra == 'memory'
47
+ Description-Content-Type: text/markdown
48
+
49
+ # forumkit
50
+
51
+ The only Python multi-agent framework with a **structured debate protocol**: fan-out → challenge → rebuttal → consensus scoring.
52
+
53
+ ```python
54
+ from forumkit import ForumModeratorBase, ResearcherBase, AgentConfig, AgentRole
55
+
56
+ class MyAnalyst(ResearcherBase):
57
+ async def analyze(self, data):
58
+ # Independent analysis
59
+ return {"position": "...", "confidence": 0.8}
60
+
61
+ async def respond_to_challenge(self, challenge, context):
62
+ # Defend your position
63
+ return "Here's why I disagree..."
64
+
65
+ class MyForum(ForumModeratorBase):
66
+ def _synthesize(self, analyses, consensus, data):
67
+ # Domain-specific consensus logic
68
+ if consensus.agreement_pct >= 66:
69
+ return "accepted", {"action": "approve"}
70
+ return "conditional", {"action": "review"}
71
+
72
+ # Run a debate
73
+ forum = MyForum(config, researchers=[analyst1, analyst2, analyst3])
74
+ result = await forum.run_debate_round({"question": "Is X good?"})
75
+
76
+ print(f"Consensus: {result.consensus.agreement_pct}% agree")
77
+ print(f"Dissent: {result.consensus.strongest_dissent}")
78
+ print(f"Anomaly?: {result.consensus.unanimous_anomaly}") # Detects groupthink
79
+ ```
80
+
81
+ ## Why forumkit?
82
+
83
+ **The problem:** CrewAI, PraisonAI, AutoGen, and others assume agents reach consensus by averaging opinions. In reality, minority views often reveal blind spots.
84
+
85
+ **The solution:** forumkit enforces dissent:
86
+ - Every agent analyzes independently
87
+ - Agents actively challenge each other's positions
88
+ - Minority opinions are surfaced, not discarded
89
+ - Suspicious unanimity is flagged (`unanimous_anomaly` detection)
90
+ - Outcomes are tied to consensus strength, not just majority vote
91
+
92
+ **The result:** Better decisions with traceable dissent.
93
+
94
+ ## Quick Start
95
+
96
+ ### Install
97
+
98
+ ```bash
99
+ pip install forumkit
100
+ ```
101
+
102
+ ### 5-minute example
103
+
104
+ ```python
105
+ import asyncio
106
+ from forumkit import ForumModeratorBase, ResearcherBase, AgentConfig, AgentRole, PersonaContext
107
+
108
+ class Analyst(ResearcherBase):
109
+ async def analyze(self, data):
110
+ # Your LLM call here
111
+ return {
112
+ "persona_id": self.persona.persona_id,
113
+ "position": f"Analysis from {self.persona.name}",
114
+ "confidence": 0.75
115
+ }
116
+
117
+ async def respond_to_challenge(self, challenge, context):
118
+ return "I maintain my position because..."
119
+
120
+ class DebateBoard(ForumModeratorBase):
121
+ def _synthesize(self, analyses, consensus, data):
122
+ synthesis = f"Consensus at {consensus.agreement_pct:.0f}%"
123
+ outcome = "approved" if consensus.agreement_pct >= 50 else "rejected"
124
+ return synthesis, outcome, None
125
+
126
+ async def main():
127
+ config = AgentConfig(
128
+ agent_id="board-1", team_id=1, role=AgentRole.HEAD,
129
+ model_alias="gpt-4"
130
+ )
131
+
132
+ # Create 3 analysts with different personas
133
+ analysts = [
134
+ Analyst(AgentConfig(...), PersonaContext(
135
+ persona_id="p1", name="Optimist", system_prompt="..."
136
+ )),
137
+ # ... more analysts
138
+ ]
139
+
140
+ board = DebateBoard(config, analysts)
141
+ result = await board.run_debate_round({"question": "Approve proposal X?"})
142
+
143
+ print(f"Outcome: {result.outcome}")
144
+ print(f"Agreement: {result.consensus.agreement_pct}%")
145
+ print(f"Dissent: {result.consensus.dissent_count} voices disagree")
146
+ print(f"Anomaly: {result.consensus.unanimous_anomaly}")
147
+
148
+ asyncio.run(main())
149
+ ```
150
+
151
+ ## Concepts
152
+
153
+ ### The Forum Protocol
154
+
155
+ 1. **Fan-out** — Every analyst independently evaluates the question
156
+ 2. **Challenge** — Analysts see each other's views and attack weak points
157
+ 3. **Rebuttal** — Each analyst responds to challenges
158
+ 4. **Score** — Consensus metrics computed: agreement %, confidence, dissent count
159
+ 5. **Synthesize** — Domain-specific outcome logic applied
160
+
161
+ ### Key Types
162
+
163
+ - **BaseAgent** — Foundation for all agents. Role-gated: `HEAD` can delegate to children, `JUNIOR` cannot call LLM
164
+ - **ResearcherBase** — Expert with persona, implements `analyze()` and `respond_to_challenge()`
165
+ - **ForumModeratorBase** — Orchestrates the debate, computes consensus, applies domain logic
166
+ - **ConsensusScore** — Result includes `agreement_pct`, `dissent_count`, **`unanimous_anomaly`** (detects suspicious unanimity)
167
+
168
+ ## Memory Backends
169
+
170
+ forumkit supports three memory types:
171
+
172
+ | Backend | Use | Backend |
173
+ |---------|-----|---------|
174
+ | **Working** | In-session cache | Redis (TTL) |
175
+ | **Episodic** | Long-term experience | PostgreSQL |
176
+ | **Semantic** | Vector embeddings | Qdrant |
177
+
178
+ All are optional. Use the facade:
179
+
180
+ ```python
181
+ from forumkit.memory import MemoryManager
182
+
183
+ memory = MemoryManager(
184
+ redis_url="redis://localhost:6379",
185
+ pg_url="postgresql://localhost/mydb",
186
+ qdrant_url="http://localhost:6333"
187
+ )
188
+
189
+ await memory.initialize()
190
+ # Use: memory.working.set(), memory.episodic.store(), memory.semantic.search()
191
+ ```
192
+
193
+ ## Docs
194
+
195
+ - **[API Reference](docs/API.md)** — All public types and methods
196
+ - **[Concepts](docs/CONCEPTS.md)** — Architecture deep-dive
197
+ - **[Examples](docs/examples/)** — Working code samples
198
+
199
+ ## Testing
200
+
201
+ ```bash
202
+ pytest tests/unit/ -v
203
+ pytest tests/integration/ -v # Requires Docker (Redis, PostgreSQL)
204
+ ```
205
+
206
+ ## Security
207
+
208
+ forumkit includes prompt injection defense:
209
+
210
+ ```python
211
+ from forumkit.security import sanitize_for_prompt
212
+
213
+ clean = sanitize_for_prompt(user_input)
214
+ # Applies: NFKC normalize, strip BiDi/control chars, remove injection patterns
215
+ ```
216
+
217
+ ## Status
218
+
219
+ **v0.1.0 — Alpha**. API is stable; docs are being refined. Not yet open source (private beta).
220
+
221
+ ## License
222
+
223
+ MIT
224
+
225
+ ## Contact
226
+
227
+ Issues: GitHub Issues
228
+ Questions: vinitpai@anthropic.com
@@ -0,0 +1,180 @@
1
+ # forumkit
2
+
3
+ The only Python multi-agent framework with a **structured debate protocol**: fan-out → challenge → rebuttal → consensus scoring.
4
+
5
+ ```python
6
+ from forumkit import ForumModeratorBase, ResearcherBase, AgentConfig, AgentRole
7
+
8
+ class MyAnalyst(ResearcherBase):
9
+ async def analyze(self, data):
10
+ # Independent analysis
11
+ return {"position": "...", "confidence": 0.8}
12
+
13
+ async def respond_to_challenge(self, challenge, context):
14
+ # Defend your position
15
+ return "Here's why I disagree..."
16
+
17
+ class MyForum(ForumModeratorBase):
18
+ def _synthesize(self, analyses, consensus, data):
19
+ # Domain-specific consensus logic
20
+ if consensus.agreement_pct >= 66:
21
+ return "accepted", {"action": "approve"}
22
+ return "conditional", {"action": "review"}
23
+
24
+ # Run a debate
25
+ forum = MyForum(config, researchers=[analyst1, analyst2, analyst3])
26
+ result = await forum.run_debate_round({"question": "Is X good?"})
27
+
28
+ print(f"Consensus: {result.consensus.agreement_pct}% agree")
29
+ print(f"Dissent: {result.consensus.strongest_dissent}")
30
+ print(f"Anomaly?: {result.consensus.unanimous_anomaly}") # Detects groupthink
31
+ ```
32
+
33
+ ## Why forumkit?
34
+
35
+ **The problem:** CrewAI, PraisonAI, AutoGen, and others assume agents reach consensus by averaging opinions. In reality, minority views often reveal blind spots.
36
+
37
+ **The solution:** forumkit enforces dissent:
38
+ - Every agent analyzes independently
39
+ - Agents actively challenge each other's positions
40
+ - Minority opinions are surfaced, not discarded
41
+ - Suspicious unanimity is flagged (`unanimous_anomaly` detection)
42
+ - Outcomes are tied to consensus strength, not just majority vote
43
+
44
+ **The result:** Better decisions with traceable dissent.
45
+
46
+ ## Quick Start
47
+
48
+ ### Install
49
+
50
+ ```bash
51
+ pip install forumkit
52
+ ```
53
+
54
+ ### 5-minute example
55
+
56
+ ```python
57
+ import asyncio
58
+ from forumkit import ForumModeratorBase, ResearcherBase, AgentConfig, AgentRole, PersonaContext
59
+
60
+ class Analyst(ResearcherBase):
61
+ async def analyze(self, data):
62
+ # Your LLM call here
63
+ return {
64
+ "persona_id": self.persona.persona_id,
65
+ "position": f"Analysis from {self.persona.name}",
66
+ "confidence": 0.75
67
+ }
68
+
69
+ async def respond_to_challenge(self, challenge, context):
70
+ return "I maintain my position because..."
71
+
72
+ class DebateBoard(ForumModeratorBase):
73
+ def _synthesize(self, analyses, consensus, data):
74
+ synthesis = f"Consensus at {consensus.agreement_pct:.0f}%"
75
+ outcome = "approved" if consensus.agreement_pct >= 50 else "rejected"
76
+ return synthesis, outcome, None
77
+
78
+ async def main():
79
+ config = AgentConfig(
80
+ agent_id="board-1", team_id=1, role=AgentRole.HEAD,
81
+ model_alias="gpt-4"
82
+ )
83
+
84
+ # Create 3 analysts with different personas
85
+ analysts = [
86
+ Analyst(AgentConfig(...), PersonaContext(
87
+ persona_id="p1", name="Optimist", system_prompt="..."
88
+ )),
89
+ # ... more analysts
90
+ ]
91
+
92
+ board = DebateBoard(config, analysts)
93
+ result = await board.run_debate_round({"question": "Approve proposal X?"})
94
+
95
+ print(f"Outcome: {result.outcome}")
96
+ print(f"Agreement: {result.consensus.agreement_pct}%")
97
+ print(f"Dissent: {result.consensus.dissent_count} voices disagree")
98
+ print(f"Anomaly: {result.consensus.unanimous_anomaly}")
99
+
100
+ asyncio.run(main())
101
+ ```
102
+
103
+ ## Concepts
104
+
105
+ ### The Forum Protocol
106
+
107
+ 1. **Fan-out** — Every analyst independently evaluates the question
108
+ 2. **Challenge** — Analysts see each other's views and attack weak points
109
+ 3. **Rebuttal** — Each analyst responds to challenges
110
+ 4. **Score** — Consensus metrics computed: agreement %, confidence, dissent count
111
+ 5. **Synthesize** — Domain-specific outcome logic applied
112
+
113
+ ### Key Types
114
+
115
+ - **BaseAgent** — Foundation for all agents. Role-gated: `HEAD` can delegate to children, `JUNIOR` cannot call LLM
116
+ - **ResearcherBase** — Expert with persona, implements `analyze()` and `respond_to_challenge()`
117
+ - **ForumModeratorBase** — Orchestrates the debate, computes consensus, applies domain logic
118
+ - **ConsensusScore** — Result includes `agreement_pct`, `dissent_count`, **`unanimous_anomaly`** (detects suspicious unanimity)
119
+
120
+ ## Memory Backends
121
+
122
+ forumkit supports three memory types:
123
+
124
+ | Backend | Use | Backend |
125
+ |---------|-----|---------|
126
+ | **Working** | In-session cache | Redis (TTL) |
127
+ | **Episodic** | Long-term experience | PostgreSQL |
128
+ | **Semantic** | Vector embeddings | Qdrant |
129
+
130
+ All are optional. Use the facade:
131
+
132
+ ```python
133
+ from forumkit.memory import MemoryManager
134
+
135
+ memory = MemoryManager(
136
+ redis_url="redis://localhost:6379",
137
+ pg_url="postgresql://localhost/mydb",
138
+ qdrant_url="http://localhost:6333"
139
+ )
140
+
141
+ await memory.initialize()
142
+ # Use: memory.working.set(), memory.episodic.store(), memory.semantic.search()
143
+ ```
144
+
145
+ ## Docs
146
+
147
+ - **[API Reference](docs/API.md)** — All public types and methods
148
+ - **[Concepts](docs/CONCEPTS.md)** — Architecture deep-dive
149
+ - **[Examples](docs/examples/)** — Working code samples
150
+
151
+ ## Testing
152
+
153
+ ```bash
154
+ pytest tests/unit/ -v
155
+ pytest tests/integration/ -v # Requires Docker (Redis, PostgreSQL)
156
+ ```
157
+
158
+ ## Security
159
+
160
+ forumkit includes prompt injection defense:
161
+
162
+ ```python
163
+ from forumkit.security import sanitize_for_prompt
164
+
165
+ clean = sanitize_for_prompt(user_input)
166
+ # Applies: NFKC normalize, strip BiDi/control chars, remove injection patterns
167
+ ```
168
+
169
+ ## Status
170
+
171
+ **v0.1.0 — Alpha**. API is stable; docs are being refined. Not yet open source (private beta).
172
+
173
+ ## License
174
+
175
+ MIT
176
+
177
+ ## Contact
178
+
179
+ Issues: GitHub Issues
180
+ Questions: vinitpai@anthropic.com