Skip to main content
Glama
README.md
# DPSCoach – AI-Powered Combat Log Analyzer for Throne & Liberty

**DPSCoach** is a desktop application and MCP toolkit that parses Throne & Liberty combat logs into actionable DPS metrics, powered by a local AI coach that answers natural-language questions about your performance using SQL-first planning and DuckDB analytics. **Prototype note:** class-specific context is not wired yet; the class dropdown is intentionally disabled until backend class filtering is implemented. **Fair-play disclaimer:** This app only reads your exported text combat logs; it does not hook, modify, or automate the game client and does not confer any in-game advantage beyond offline analytics.

---

## 🎬 Demo

![DPSCoach Demo](docs/demo.gif)

<p align="center">
       <img src="visuals/dps-coach.png" alt="DPSCoach UI" width="420" />
       <img src="visuals/runs.png" alt="Runs table" width="420" />
</p>

> **TODO:** Record a 30-second GIF showing: log analysis β†’ coach question β†’ SQL trace β†’ answer. See [docs/DEMO.md](docs/DEMO.md) for recording instructions.

---

## ✨ Features

- **AI Coach with Intent Routing**: Ask "Why did my crit rate drop?" and the coach detects intent (CRIT_BUCKET_TREND, SKILL_DELTA, RUNS, etc.) and routes to deterministic handlers or LLM-planned SQL queries.
- **Single-Call MCP Analysis Packet**: All metrics (run summary, top skills, timeline buckets, skill deltas, windows, action levers) returned in one `get_analysis_packet` callβ€”no iterative prompting required.
- **DuckDB Event Store**: Combat events loaded into an in-memory DuckDB table for fast, safe, read-only SQL queries via MCP tools (`query_dps`, `get_events_schema`).
- **Strict Model Verification**: GGUF model integrity enforced via SHA-256 hash and minimum file size checks; models downloaded on-demand to user app data (never bundled).
- **Read-Only Safety**: All tools are SELECT-only; no INSERT/UPDATE/DELETE/file writes. Caps on result sizes (50 runs, 200 timeline buckets) prevent resource abuse.
- **Deterministic Fallbacks**: If the LLM misbehaves, the coach falls back to a safe default query and still produces an answerβ€”never displays instructions to "use Quick Questions."
- **PySide6 Desktop UI**: Native Windows app with tabbed views (Summary, Runs, Skills, Coach) and background workers for non-blocking analysis.
- **PyInstaller One-Click Build**: Ships as a standalone EXE with Python runtime and all dependencies (except the GGUF model, which downloads on first launch).
- **Class Context (coming soon)**: UI shows class dropdown today; backend class-filtered analysis will land in the next iteration.
- **Fair Play**: Reads UTF-8 combat log files only; no game hooks, memory reads, packet interception, or automation.

---

## πŸ—οΈ Architecture

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  PySide6 UI  β”‚  (main.py)
β”‚  (Desktop)   β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚ QPushButton clicks
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   SQLCoach       β”‚  (coach_local.py)
β”‚   Intent Router  β”‚  ─ Detects RUNS, SKILLS, CRIT_BUCKET_TREND, etc.
β”‚   + LLM Planner  β”‚  ─ Plans SQL or returns deterministic answer
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚ MCP client calls
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  MCP Client      β”‚  (mcp_client.py)
β”‚  (Subprocess)    β”‚  ─ Spawns `python -m mcp_server`
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚ stdio JSON-RPC
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  MCP Server      β”‚  (mcp_server.py, FastMCP)
β”‚  + DuckDB Store  β”‚  ─ analyze_dps_logs, query_dps, get_analysis_packet
β”‚  (event_store)   β”‚  ─ Safe SELECT templates, no mutations
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚ read logs
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Combat Logs     β”‚  (UTF-8 .txt/.log files)
β”‚  (TL SAVED dir)  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

**Key Design Choices:**

- **MCP as the contract boundary**: UI and coach communicate via MCP tools, ensuring the same payload shape for CLI, desktop, and future integrations.
- **Intent-first routing**: Deterministic handlers (90% of questions) bypass the LLM entirely, guaranteeing consistent formatting and trace clarity.
- **DuckDB instead of pandas**: In-memory SQL engine enables ad-hoc queries without shipping raw events to the UI; query results are capped and safe.

---

## πŸ”’ Safety Guarantees

- **Read-Only Tools**: `query_dps` enforces SELECT-only via AST parse; INSERT/UPDATE/DELETE raise exceptions.
- **Clamped Parameters**: `last_n_runs` limited to [1, 50], `top_k_skills` to [1, 50], `bucket_seconds` coerced to allowed values (1, 2, 5, 10, 15, 30, 60).
- **No File Writes in Tools**: The MCP server never writes files; all outputs go to stdout (CLI) or returned as JSON (tools).
- **Model Integrity**: GGUF files must pass SHA-256 hash and minimum size checks before loading; corrupt or tampered models are rejected.
- **Deterministic Self-Test**: Model must respond "OK" to a trivial prompt before the UI enables the coach chat.

---

## 🎯 Engineering Signals (for hiring managers)

This project demonstrates professional software engineering practices suitable for production systems:

### 1. **Contract-Driven Design**
- **MCP as API boundary**: UI, CLI, and third-party clients consume identical JSON payloads.
- **Stable schemas**: `runs_last_n` normalized to `list[dict]` with explicit keys (`run_id`, `dps`, `total_damage`, etc.); consumers never rely on positional indexes.
- **Test parity**: Smoke tests (`smoke_mcp_tool.py`) verify MCP tool output matches CLI output (modulo `generated_at` timestamps).

### 2. **Defensive Programming & Validation**
- **Input sanitization**: SQL inputs quoted via `.replace("'", "''")`, user file paths resolved with `Path(...).expanduser()`.
- **Schema enforcement**: Combat logs with unexpected column counts are skipped (not fatal); parsers yield instead of loading entire files into memory.
- **Graceful degradation**: Missing or corrupt models trigger fallback UIs; malformed LLM outputs route to deterministic handlers.

### 3. **Testability & Observability**
- **73 unit tests** covering intent detection, route handlers, DPS bound checks, session persistence, skill delta rendering.
- **Trace logging**: Every coach answer includes a tool trace showing which MCP calls were made, with counts for runs/skills/timeline buckets.
- **Reproducible builds**: `scripts/test_all.ps1` runs all tests + smoke checks in one command; CI/CD-ready.

### 4. **Performance & Resource Management**
- **Streaming parsers**: Log files parsed as iterators (`yield` per run) to avoid loading 100MB+ files into RAM.
- **Background threads**: Qt workers (`QThread`) for model downloads, analysis, and coach inference keep the UI responsive.
- **DuckDB in-memory**: Query results are row-limited and columnar; no unbounded memory growth.

### 5. **Security & Isolation**
- **No shell=True**: Model download uses `urllib.request`, MCP client spawns `python -m mcp_server` safely.
- **Subprocess sandboxing**: MCP server runs in a child process; UI never directly touches combat log files.
- **User-controlled models**: GGUF weights stored in `%APPDATA%\DPSCoach\models\`, never bundled in the EXE, so users verify/replace files independently.

### 6. **User Experience & Polish**
- **Intent-aware routing**: 90% of questions (RUNS, SKILLS, CRIT_BUCKET_TREND) skip the LLM and return instant, deterministic answers.
- **Self-documenting UI**: "Quick Questions" buttons demonstrate capabilities; SQL trace shows exactly what was queried.
- **Transparent errors**: Model validation failures display the exact error message; "Copy Error" button for support requests.

### 7. **Maintainable Codebase**
- **Modular architecture**: Parser (`dps_logs/parser.py`), metrics (`metrics.py`), reporting (`reporting.py`), server (`server.py`), UI (`app/main.py`) are independently testable.
- **Type hints**: All functions annotated with `-> Dict[str, Any]`, `Optional[str]`, etc.; mypy-compatible.
- **Docstrings**: Public APIs documented with Google-style docstrings; test names are descriptive (e.g., `test_runs_analysis_dps_not_exceeding_bound`).

---

## πŸš€ Setup & Installation

### Prerequisites
- **Python 3.11+** (tested with Windows default install)
- **Git** (for cloning the repo)
- **Throne & Liberty** combat logs exported as UTF-8 `.txt` or `.log` files

### Development Install

```powershell
# Clone the repository
git clone https://github.com/stalcup-dev/tl-dps-mcp.git
cd tl-dps-mcp

# Install all dependencies (core + dev + UI + coach)
pip install -r requirements.txt

# Install coach-specific dependencies (llama-cpp-python)
pip install -r app/requirements_coach.txt

# Run CLI smoke test
python -m server --sample --pretty

# Run desktop app
python -m app.main

# (Optional) Showcase for data analytics skill growth
# - Swap log directories to compare player sessions and build a portfolio of analyses
# - Export JSON/MD outputs to demonstrate exploratory data analysis and reporting
# - Extend the MCP server with custom tools to highlight SQL + Python + DuckDB skills
```

### Model Download

The desktop app will prompt to download the required GGUF model (~4.4 GB) on first launch. Models are stored at:

```
%APPDATA%\DPSCoach\models\model.gguf
```

Alternatively, download manually:
- **Primary model**: [Qwen2.5-7B-Instruct Q4_K_M](https://huggingface.co/bartowski/Qwen2.5-7B-Instruct-GGUF/resolve/main/Qwen2.5-7B-Instruct-Q4_K_M.gguf)
- Place in `models/model.gguf` (repo root) or `%APPDATA%\DPSCoach\models\model.gguf`

### Where Combat Logs Are Stored

Throne & Liberty saves logs to:

```
%LOCALAPPDATA%\TL\Saved\COMBATLOGS\
```

Point the UI or CLI at this directory to analyze your recent runs.

---

## πŸ§ͺ Running Tests

**Official test contract** (single command for all tests):

```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\test_all.ps1
```

> Tip for your analytics portfolio: include the test contract in writeups to demonstrate reliability and reproducibility alongside your data insights.

This runs:
1. `python -m unittest discover -s tests -v` (73 unit tests)
2. `python -m tests.smoke_mcp_tool --sample` (MCP parity check)

**Additional validation:**

```powershell
# Verify DPS math
python validate_output.py --sample

# Packaged build smoke test
python -m server --smoke-packaged
```

---

## πŸ“– Usage Examples

### CLI Analysis

```powershell
# Analyze bundled sample log
python -m server --sample --pretty

# Analyze your own logs (newest 5 runs)
python -m server --input-path %LOCALAPPDATA%\TL\Saved\COMBATLOGS --limit-runs 5 --output-json reports/latest.json --output-md reports/latest.md

# Smoke test (verifies MCP tools + DuckDB queries)
python -m server --smoke-packaged
```

### CLI Options

| Flag | Description |
| --- | --- |
| `--input-path PATH` | File or directory containing TL combat logs. Defaults to `sample_data/example_log_01.txt`. |
| `--sample` | Shortcut to always use the bundled sample log. |
| `--pretty` | Pretty-print the JSON output using an indent of 2 spaces. |
| `--limit-runs N` | When pointing at a directory, only parse the newest `N` `.txt`/`.log` files. |
| `--output-json PATH` | Write the full JSON payload (always indented) to the given path while still printing to stdout. |
| `--output-md PATH` | Write a Markdown report built from the JSON payload. |
| `--smoke-packaged` | Run the bundled smoke workflow, write `reports/packaged_smoke.json`, and print `SMOKE PASS`/`SMOKE FAIL`. |

### MCP Tool (from Claude Desktop or other MCP clients)

Add to your MCP config (`claude_desktop_config.json` or similar):

```json
{
  "mcpServers": {
    "tl-dps-mcp": {
      "command": "python",
      "args": ["-m", "mcp_server"],
      "cwd": "C:\\Users\\Allen\\Desktop\\Data Analyst Projects\\tl-dps-mcp"
    }
  }
}
```

Then ask Claude:
- "Analyze my TL logs at C:\Users\...\COMBATLOGS"
- "What's my average DPS across the last 10 runs?"
- "Show me skill efficiency for run_123"

### Desktop App Workflow

1. Launch `python -m app.main`
2. Click "Download Model" (one-time, ~4.4 GB)
3. After model self-test passes, click "Browse Combat Logs Directory"
4. Select your `COMBATLOGS` folder
5. Click "Analyze Logs"
6. Switch to **Coach** tab and ask questions like:
   - "Why did my crit rate drop?"
   - "Which skill fell off?"
   - "Show me my best run"

---

## πŸ—ΊοΈ Roadmap

- [ ] **Multi-Run Comparisons**: Side-by-side view of best vs. worst runs with delta highlights.
- [ ] **Rotation Suggestions**: Detect opener sequences and suggest reordering based on early damage frontloading.
- [ ] **Benchmarks & Percentiles**: Compare your DPS to class/spec benchmarks (user-submitted data or scraped leaderboards).
- [ ] **Export Report**: One-click PDF/HTML export with charts (DPS over time, skill breakdown pie chart, crit rate timeline).
- [ ] **Performance Optimizations**: Stream timeline buckets to SQLite on disk for sessions >1000 runs; add indexes for skill_name queries.

---

## πŸ™Œ Help Wanted: Class Data

If you want to contribute, the biggest need is class reference data in plain text for **every Throne & Liberty class**:
- Class benefits and unique passives
- All skills with descriptions (damage types, DoT limits, cooldowns)
- Known combos/rotations and synergy notes
- Edge cases: caps on stacking or DoT application limits

Any text format works (TXT/MD/CSV). Drop links or files via an issue or PR so we can wire class-aware analysis faster.

---

## πŸ“œ License

*This project does not yet have a license file. A permissive open-source license (MIT or Apache 2.0) will be added before public release.*

---

## πŸ™ Acknowledgments

- **Throne & Liberty** by NCSoft for the combat log format.
- **FastMCP** for the Model Context Protocol server framework.
- **DuckDB** for the in-memory SQL engine.
- **llama.cpp** and **llama-cpp-python** for local GGUF inference.
- **Qwen2.5-7B-Instruct** (Alibaba) for the coach model weights.

---

## πŸ“¬ Contact & Links

- **GitHub**: [github.com/stalcup-dev/tl-dps-mcp](https://github.com/stalcup-dev/tl-dps-mcp)
- **Author Email**: allen.stalc@gmail.com
- **Author GitHub**: [github.com/stalcup-dev](https://github.com/stalcup-dev)
- **Issues**: [github.com/stalcup-dev/tl-dps-mcp/issues](https://github.com/stalcup-dev/tl-dps-mcp/issues)

---

**Built by a senior engineer who cares about contracts, testing, and user experience.** πŸš€