- Add vector store with sentence-transformers for semantic search - FastAPI backend with /chat and /health endpoints - Conversation state persistence via SQLite - OpenRouter integration with structured JSON responses - Discord bot with /ask slash command and reply-based follow-ups - Automated Gitea issue creation for unanswered questions - Docker support with docker-compose for easy deployment - Example rule file and ingestion script - Comprehensive documentation in README
230 lines
6.6 KiB
Markdown
230 lines
6.6 KiB
Markdown
# 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
|