Metadata-Version: 2.4
Name: forumkit
Version: 0.1.0
Summary: Multi-agent debate framework with structured consensus scoring and dissent tracking
Project-URL: Homepage, https://github.com/vinitpai/forumkit
Project-URL: Repository, https://github.com/vinitpai/forumkit
Project-URL: Issues, https://github.com/vinitpai/forumkit/issues
License: MIT
License-File: LICENSE
Keywords: ai-agents,consensus,debate,llm,multi-agent
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.11
Requires-Dist: aioredis>=2.0
Requires-Dist: asyncpg>=0.29
Requires-Dist: httpx>=0.27
Requires-Dist: litellm>=1.40
Requires-Dist: pydantic>=2.0
Requires-Dist: structlog>=24.0
Provides-Extra: all
Requires-Dist: aioredis>=2.0; extra == 'all'
Requires-Dist: asyncpg>=0.29; extra == 'all'
Requires-Dist: httpx>=0.27; extra == 'all'
Requires-Dist: kafka-python>=2.0; extra == 'all'
Requires-Dist: minio>=7.0; extra == 'all'
Requires-Dist: neo4j>=5.0; extra == 'all'
Provides-Extra: dev
Requires-Dist: coverage>=7.0; extra == 'dev'
Requires-Dist: mypy>=1.9; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-mock>=3.12; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Provides-Extra: jepa
Requires-Dist: minio>=7.0; extra == 'jepa'
Requires-Dist: neo4j>=5.0; extra == 'jepa'
Provides-Extra: kafka
Requires-Dist: kafka-python>=2.0; extra == 'kafka'
Provides-Extra: memory
Requires-Dist: aioredis>=2.0; extra == 'memory'
Requires-Dist: asyncpg>=0.29; extra == 'memory'
Requires-Dist: httpx>=0.27; extra == 'memory'
Description-Content-Type: text/markdown

# forumkit

The only Python multi-agent framework with a **structured debate protocol**: fan-out → challenge → rebuttal → consensus scoring.

```python
from forumkit import ForumModeratorBase, ResearcherBase, AgentConfig, AgentRole

class MyAnalyst(ResearcherBase):
    async def analyze(self, data):
        # Independent analysis
        return {"position": "...", "confidence": 0.8}

    async def respond_to_challenge(self, challenge, context):
        # Defend your position
        return "Here's why I disagree..."

class MyForum(ForumModeratorBase):
    def _synthesize(self, analyses, consensus, data):
        # Domain-specific consensus logic
        if consensus.agreement_pct >= 66:
            return "accepted", {"action": "approve"}
        return "conditional", {"action": "review"}

# Run a debate
forum = MyForum(config, researchers=[analyst1, analyst2, analyst3])
result = await forum.run_debate_round({"question": "Is X good?"})

print(f"Consensus: {result.consensus.agreement_pct}% agree")
print(f"Dissent: {result.consensus.strongest_dissent}")
print(f"Anomaly?: {result.consensus.unanimous_anomaly}")  # Detects groupthink
```

## Why forumkit?

**The problem:** CrewAI, PraisonAI, AutoGen, and others assume agents reach consensus by averaging opinions. In reality, minority views often reveal blind spots.

**The solution:** forumkit enforces dissent:
- Every agent analyzes independently
- Agents actively challenge each other's positions
- Minority opinions are surfaced, not discarded
- Suspicious unanimity is flagged (`unanimous_anomaly` detection)
- Outcomes are tied to consensus strength, not just majority vote

**The result:** Better decisions with traceable dissent.

## Quick Start

### Install

```bash
pip install forumkit
```

### 5-minute example

```python
import asyncio
from forumkit import ForumModeratorBase, ResearcherBase, AgentConfig, AgentRole, PersonaContext

class Analyst(ResearcherBase):
    async def analyze(self, data):
        # Your LLM call here
        return {
            "persona_id": self.persona.persona_id,
            "position": f"Analysis from {self.persona.name}",
            "confidence": 0.75
        }

    async def respond_to_challenge(self, challenge, context):
        return "I maintain my position because..."

class DebateBoard(ForumModeratorBase):
    def _synthesize(self, analyses, consensus, data):
        synthesis = f"Consensus at {consensus.agreement_pct:.0f}%"
        outcome = "approved" if consensus.agreement_pct >= 50 else "rejected"
        return synthesis, outcome, None

async def main():
    config = AgentConfig(
        agent_id="board-1", team_id=1, role=AgentRole.HEAD,
        model_alias="gpt-4"
    )

    # Create 3 analysts with different personas
    analysts = [
        Analyst(AgentConfig(...), PersonaContext(
            persona_id="p1", name="Optimist", system_prompt="..."
        )),
        # ... more analysts
    ]

    board = DebateBoard(config, analysts)
    result = await board.run_debate_round({"question": "Approve proposal X?"})

    print(f"Outcome: {result.outcome}")
    print(f"Agreement: {result.consensus.agreement_pct}%")
    print(f"Dissent: {result.consensus.dissent_count} voices disagree")
    print(f"Anomaly: {result.consensus.unanimous_anomaly}")

asyncio.run(main())
```

## Concepts

### The Forum Protocol

1. **Fan-out** — Every analyst independently evaluates the question
2. **Challenge** — Analysts see each other's views and attack weak points
3. **Rebuttal** — Each analyst responds to challenges
4. **Score** — Consensus metrics computed: agreement %, confidence, dissent count
5. **Synthesize** — Domain-specific outcome logic applied

### Key Types

- **BaseAgent** — Foundation for all agents. Role-gated: `HEAD` can delegate to children, `JUNIOR` cannot call LLM
- **ResearcherBase** — Expert with persona, implements `analyze()` and `respond_to_challenge()`
- **ForumModeratorBase** — Orchestrates the debate, computes consensus, applies domain logic
- **ConsensusScore** — Result includes `agreement_pct`, `dissent_count`, **`unanimous_anomaly`** (detects suspicious unanimity)

## Memory Backends

forumkit supports three memory types:

| Backend | Use | Backend |
|---------|-----|---------|
| **Working** | In-session cache | Redis (TTL) |
| **Episodic** | Long-term experience | PostgreSQL |
| **Semantic** | Vector embeddings | Qdrant |

All are optional. Use the facade:

```python
from forumkit.memory import MemoryManager

memory = MemoryManager(
    redis_url="redis://localhost:6379",
    pg_url="postgresql://localhost/mydb",
    qdrant_url="http://localhost:6333"
)

await memory.initialize()
# Use: memory.working.set(), memory.episodic.store(), memory.semantic.search()
```

## Docs

- **[API Reference](docs/API.md)** — All public types and methods
- **[Concepts](docs/CONCEPTS.md)** — Architecture deep-dive
- **[Examples](docs/examples/)** — Working code samples

## Testing

```bash
pytest tests/unit/ -v
pytest tests/integration/ -v  # Requires Docker (Redis, PostgreSQL)
```

## Security

forumkit includes prompt injection defense:

```python
from forumkit.security import sanitize_for_prompt

clean = sanitize_for_prompt(user_input)
# Applies: NFKC normalize, strip BiDi/control chars, remove injection patterns
```

## Status

**v0.1.0 — Alpha**. API is stable; docs are being refined. Not yet open source (private beta).

## License

MIT

## Contact

Issues: GitHub Issues
Questions: vinitpai@anthropic.com
