# Strat-Chatbot AI-powered Q&A chatbot for Strat-O-Matic baseball league rules. ## Features - **Natural language Q&A**: Ask questions about league rules in plain English - **Semantic search**: Uses ChromaDB vector embeddings to find relevant rules - **Rule citations**: Always cites specific rule IDs (e.g., "Rule 5.2.1(b)") - **Conversation threading**: Maintains conversation context for follow-up questions - **Gitea integration**: Automatically creates issues for unanswered questions - **Discord integration**: Slash command `/ask` with reply-based follow-ups ## Architecture ``` ┌─────────┐ ┌──────────────┐ ┌─────────────┐ │ Discord │────│ FastAPI │────│ ChromaDB │ │ Bot │ │ (port 8000) │ │ (vectors) │ └─────────┘ └──────────────┘ └─────────────┘ │ ┌───────▼──────┐ │ Markdown │ │ Rule Files │ └──────────────┘ │ ┌───────▼──────┐ │ OpenRouter │ │ (LLM API) │ └──────────────┘ │ ┌───────▼──────┐ │ Gitea │ │ Issues │ └──────────────┘ ``` ## Quick Start ### Prerequisites - Docker & Docker Compose - OpenRouter API key - Discord bot token - Gitea token (optional, for issue creation) ### Setup 1. **Clone and configure** ```bash cd strat-chatbot cp .env.example .env # Edit .env with your API keys and tokens ``` 2. **Prepare rules** Place your rule documents in `data/rules/` as Markdown files with YAML frontmatter: ```markdown --- rule_id: "5.2.1(b)" title: "Stolen Base Attempts" section: "Baserunning" parent_rule: "5.2" page_ref: "32" --- When a runner attempts to steal... ``` 3. **Ingest rules** ```bash # With Docker Compose (recommended) docker compose up -d docker compose exec api python scripts/ingest_rules.py # Or locally uv sync uv run scripts/ingest_rules.py ``` 4. **Start services** ```bash docker compose up -d ``` The API will be available at http://localhost:8000 The Discord bot will connect and sync slash commands. ### Runtime Configuration | Environment Variable | Required? | Description | |---------------------|-----------|-------------| | `OPENROUTER_API_KEY` | Yes | OpenRouter API key | | `OPENROUTER_MODEL` | No | Model ID (default: `stepfun/step-3.5-flash:free`) | | `DISCORD_BOT_TOKEN` | No | Discord bot token (omit to run API only) | | `DISCORD_GUILD_ID` | No | Guild ID for slash command sync (faster than global) | | `GITEA_TOKEN` | No | Gitea API token (for issue creation) | | `GITEA_OWNER` | No | Gitea username (default: `cal`) | | `GITEA_REPO` | No | Repository name (default: `strat-chatbot`) | ## API Endpoints | Endpoint | Method | Description | |----------|--------|-------------| | `/health` | GET | Health check with stats | | `/chat` | POST | Send a question and get a response | | `/stats` | GET | Knowledge base and system statistics | ### Chat Request ```json { "message": "Can a runner steal on a 2-2 count?", "user_id": "123456789", "channel_id": "987654321", "conversation_id": "optional-uuid", "parent_message_id": "optional-parent-uuid" } ``` ### Chat Response ```json { "response": "Yes, according to Rule 5.2.1(b)...", "conversation_id": "conv-uuid", "message_id": "msg-uuid", "cited_rules": ["5.2.1(b)", "5.3"], "confidence": 0.85, "needs_human": false } ``` ## Development ### Local Development (without Docker) ```bash # Install dependencies uv sync # Ingest rules uv run scripts/ingest_rules.py # Run API server uv run app/main.py # In another terminal, run Discord bot uv run app/discord_bot.py ``` ### Project Structure ``` strat-chatbot/ ├── app/ │ ├── __init__.py │ ├── config.py # Configuration management │ ├── database.py # SQLAlchemy conversation state │ ├── gitea.py # Gitea API client │ ├── llm.py # OpenRouter integration │ ├── main.py # FastAPI app │ ├── models.py # Pydantic models │ ├── vector_store.py # ChromaDB wrapper │ └── discord_bot.py # Discord bot ├── data/ │ ├── chroma/ # Vector DB (auto-created) │ └── rules/ # Your markdown rule files ├── scripts/ │ └── ingest_rules.py # Ingestion pipeline ├── tests/ # Test files ├── .env.example ├── Dockerfile ├── docker-compose.yml └── pyproject.toml ``` ## Performance Optimizations - **Embedding cache**: ChromaDB persists embeddings on disk - **Rule chunking**: Each rule is a separate document, no context fragmentation - **Top-k search**: Configurable number of rules to retrieve (default: 10) - **Conversation TTL**: 30 minutes to limit database size - **Async operations**: All I/O is non-blocking ## Testing the API ```bash curl -X POST http://localhost:8000/chat \ -H "Content-Type: application/json" \ -d '{ "message": "What happens if the pitcher balks?", "user_id": "test123", "channel_id": "general" }' ``` ## Gitea Integration When the bot encounters a question it can't answer confidently (confidence < 0.4), it will automatically: 1. Log the question to console 2. Create an issue in your configured Gitea repo 3. Include: user ID, channel, question, attempted rules, conversation link Issues are labeled with: - `rules-gap` - needs a rule addition or clarification - `ai-generated` - created by AI bot - `needs-review` - requires human administrator attention ## To-Do - [ ] Build OpenRouter Docker client with proper torch dependencies - [ ] Add PDF ingestion support (convert PDF → Markdown) - [ ] Implement rule change detection and incremental updates - [ ] Add rate limiting per Discord user - [ ] Create admin endpoints for rule management - [ ] Add Prometheus metrics for monitoring - [ ] Build unit and integration tests ## License TBD