maia3-chess-api / api-spec.md
anandu467's picture
Add tactical vision and game review endpoints
fa5dd4c
|
Raw
History Blame Contribute Delete
18.5 kB
# Maia3 Chess API Specification
Base URL: `https://<your-space>.hf.space`
Authentication: All endpoints (except `/api/health` and `GET /api/tactical-vision`) require an `x-api-key` header.
```
x-api-key: sk-maia3-2026
```
---
## Endpoints
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/health` | Health check |
| `POST` | `/api/bestmove` | Best move prediction |
| `POST` | `/api/analyze-move` | Analyze a specific move |
| `POST` | `/api/generate-puzzle` | Generate a tactical puzzle |
| `GET` | `/api/categories` | List puzzle categories |
| `POST` | `/api/tactical-vision` | Full tactical vision (model) |
| `GET` | `/api/tactical-vision` | Lightweight overlay (no model) |
| `POST` | `/api/game-review` | Post-game analysis |
---
## `POST /api/tactical-vision`
Real-time board overlay during a live game. Helps the human player see move probabilities, tactical threats, and danger assessment.
### Request
```json
{
"fen": "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1",
"elo_self": 1500,
"elo_oppo": 1500,
"top_n": 10,
"player_color": "black",
"bot_last_move": "e2e4",
"compare_elos": true
}
```
| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `fen` | string | yes | — | FEN string of the current position |
| `elo_self` | int | no | 1500 | Player's Elo rating (0–5000) |
| `elo_oppo` | int | no | 1500 | Opponent's Elo rating (0–5000) |
| `top_n` | int | no | 10 | Number of top moves to return (1–20) |
| `player_color` | string | no | `"white"` | Player's side: `"white"` or `"black"` |
| `bot_last_move` | string | no | null | UCI of opponent's last move to analyze. Use the FEN from **before** the opponent's move for accurate analysis. |
| `compare_elos` | bool | no | false | Include Elo comparison across 1200/1500/1800/2100/2400 |
### Response
```json
{
"fen": "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1",
"elo_self": 1500,
"elo_oppo": 1500,
"player_side": "black",
"position": {
"win_probability": 0.4437,
"eval_cp": -39,
"confidence": "confusing",
"entropy": 3.142,
"max_probability": 0.425,
"legal_move_count": 20,
"danger_level": "low",
"danger_detail": "Position is relatively straightforward."
},
"top_moves": [
{
"uci": "e7e5",
"san": "e5",
"probability": 0.4247,
"rank": 1,
"classification": "good",
"result_eval_cp": 5,
"result_win_probability": 0.5063,
"eval_delta_cp": 44,
"is_capture": false,
"is_check": false,
"is_mate": false,
"is_tempting_blunder": false,
"tactical_tags": []
},
{
"uci": "e7e6",
"san": "e6",
"probability": 0.1220,
"rank": 2,
"classification": "mistake",
"result_eval_cp": 1,
"result_win_probability": 0.5013,
"eval_delta_cp": 40,
"is_capture": false,
"is_check": false,
"is_mate": false,
"is_tempting_blunder": false,
"tactical_tags": []
}
],
"threats": {
"hanging_pieces": [],
"attacked_pieces": [],
"underdefended_squares": [],
"king_safety_concern": false,
"king_safety_detail": ""
},
"tactical_alerts": [
{
"type": "hanging_piece",
"square": "e4",
"piece": "pawn",
"description": "Undefended white pawn on e4"
}
],
"bot_last_move": {
"uci": "e2e4",
"san": "e4",
"probability": 0.5200,
"rank": 1,
"classification": "excellent"
},
"elo_comparison": {
"1200": {
"best_move": "e7e5",
"best_probability": 0.4985,
"top_moves": [
{ "uci": "e7e5", "san": "e5", "probability": 0.4985 },
{ "uci": "e7e6", "san": "e6", "probability": 0.1320 }
]
},
"1500": {
"best_move": "e7e5",
"best_probability": 0.4247,
"top_moves": [
{ "uci": "e7e5", "san": "e5", "probability": 0.4247 }
]
},
"1800": {
"best_move": "e7e5",
"best_probability": 0.3875,
"top_moves": [
{ "uci": "e7e5", "san": "e5", "probability": 0.3875 }
]
},
"2100": {
"best_move": "e7e5",
"best_probability": 0.3039,
"top_moves": [
{ "uci": "e7e5", "san": "e5", "probability": 0.3039 }
]
},
"2400": {
"best_move": "e7e5",
"best_probability": 0.2654,
"top_moves": [
{ "uci": "e7e5", "san": "e5", "probability": 0.2654 }
]
}
}
}
```
### Field Reference
#### `position`
| Field | Type | Description |
|-------|------|-------------|
| `win_probability` | float | Maia3's predicted win probability for the side to move (0–1) |
| `eval_cp` | int | Centipawn evaluation derived from win probability |
| `confidence` | string | `"clear"` (one move dominates), `"complex"` (top move 50–85%), `"confusing"` (no clear favorite), `"terminal"` (game over) |
| `entropy` | float | Shannon entropy of the policy distribution over legal moves |
| `max_probability` | float | Probability of the top move |
| `legal_move_count` | int | Number of legal moves |
| `danger_level` | string | `"low"`, `"moderate"`, or `"high"` — how treacherous the position is |
| `danger_detail` | string | Human-readable explanation of the danger level |
#### `top_moves[]`
| Field | Type | Description |
|-------|------|-------------|
| `uci` | string | Move in UCI format |
| `san` | string | Move in Standard Algebraic Notation |
| `probability` | float | Maia3's predicted probability a human at this Elo plays this move |
| `rank` | int | Rank among all legal moves (1 = most likely) |
| `classification` | string | `"excellent"` (≥60%), `"good"` (≥30%), `"inaccuracy"` (≥15%), `"mistake"` (≥5%), `"blunder"` (<5%) |
| `result_eval_cp` | int | Evaluation after playing this move (centipawns) |
| `result_win_probability` | float | Win probability after playing this move |
| `eval_delta_cp` | int | Change in evaluation: positive = improves position, negative = worsens |
| `is_capture` | bool | Whether the move is a capture |
| `is_check` | bool | Whether the move gives check |
| `is_mate` | bool | Whether the move delivers checkmate |
| `is_tempting_blunder` | bool | High probability (≥15%) but leads to a significantly worse position (eval_delta < -100cp) |
| `tactical_tags` | string[] | Tactical motifs detected: `"fork"`, `"pin"`, `"skewer"`, `"discovered_attack"`, `"hanging_piece"`, `"trapped_piece"`, `"sacrifice"`, `"zwischenzug"`, `"king_safety"` |
#### `threats`
| Field | Type | Description |
|-------|------|-------------|
| `hanging_pieces` | string[] | Squares of opponent's undefended pieces |
| `attacked_pieces` | string[] | Squares of your attacked but undefended pieces |
| `underdefended_squares` | string[] | Squares where attacker value exceeds defender value |
| `king_safety_concern` | bool | Whether there's a king safety issue |
| `king_safety_detail` | string | Description of the king safety concern |
#### `tactical_alerts[]`
| Field | Type | Description |
|-------|------|-------------|
| `type` | string | `"hanging_piece"`, `"attacked_piece"`, `"in_check"`, `"fork_opportunity"` |
| `square` | string | Square name (e.g., `"e4"`) |
| `piece` | string | Piece type (e.g., `"pawn"`, `"knight"`) |
| `description` | string | Human-readable description |
#### `bot_last_move`
Only present when `bot_last_move` is provided in the request.
| Field | Type | Description |
|-------|------|-------------|
| `uci` | string | The move in UCI format |
| `san` | string | The move in SAN |
| `probability` | float | How likely Maia3 thought a human at this Elo would play this move |
| `rank` | int | Rank among all legal moves |
| `classification` | string | Move quality classification |
#### `elo_comparison`
Only present when `compare_elos: true`. Keys are Elo levels (1200, 1500, 1800, 2100, 2400).
| Field | Type | Description |
|-------|------|-------------|
| `best_move` | string | Top move at this Elo |
| `best_probability` | float | Probability of the top move |
| `top_moves` | array | Top 5 moves at this Elo |
---
## `GET /api/tactical-vision`
Lightweight chess-only overlay — **no model inference**. Use for fast board overlays showing threats and tactical alerts.
### Parameters
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `fen` | string | yes | — | FEN string |
| `player_color` | string | no | `"white"` | Player's side |
### Response
```json
{
"fen": "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1",
"player_side": "black",
"threats": {
"hanging_pieces": ["e4"],
"attacked_pieces": [],
"underdefended_squares": [],
"king_safety_concern": false,
"king_safety_detail": ""
},
"tactical_alerts": [
{
"type": "hanging_piece",
"square": "e4",
"piece": "pawn",
"description": "Undefended white pawn on e4"
}
],
"board_info": {
"turn": "black",
"fullmove": 1,
"halfmove_clock": 0,
"is_check": false,
"is_game_over": false,
"piece_count": 31
}
}
```
---
## `POST /api/game-review`
Post-game analysis. Reviews every move in a completed game, calculates consistency, identifies blunder patterns, and provides recommendations.
### Request
Provide either `pgn` or `moves`:
**Option A: PGN**
```json
{
"pgn": "[Event \"Casual\"]\n[White \"Player1\"]\n[Black \"Player2\"]\n[Result \"1-0\"]\n[WhiteElo \"1500\"]\n[BlackElo \"1480\"]\n\n1. e4 e5 2. Bc4 Nc6 3. Qh5 Nf6 4. Qxf7# 1-0",
"elo_white": 1500,
"elo_black": 1480
}
```
**Option B: Move list**
```json
{
"moves": ["e2e4", "e7e5", "f1c4", "b8c6", "d1h5", "g8f6", "h5f7"],
"starting_fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"elo_white": 1500,
"elo_black": 1500
}
```
| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `pgn` | string | one of pgn/moves | null | PGN string of the game |
| `moves` | string[] | one of pgn/moves | null | List of UCI moves |
| `starting_fen` | string | no | starting position | Starting FEN |
| `elo_white` | int | no | 1500 | White player's Elo |
| `elo_black` | int | no | 1500 | Black player's Elo |
### Response
```json
{
"game_info": {
"white": "Player1",
"black": "Player2",
"result": "1-0",
"elo_white": 1500,
"elo_black": 1480,
"total_moves": 7,
"opening": ""
},
"summary": {
"consistency_score": 0.34,
"consistency_label": "Below your usual level",
"average_move_probability": 0.34,
"blunder_count": 1,
"mistake_count": 1,
"inaccuracy_count": 2,
"good_count": 1,
"excellent_count": 2,
"missed_opportunities": 0
},
"phase_breakdown": {
"opening": {
"moves": 7,
"consistency_score": 0.34,
"average_probability": 0.34,
"blunders": 1,
"mistakes": 1,
"inaccuracies": 2,
"good": 1,
"excellent": 2,
"label": "Opening technique needs work"
},
"middlegame": {
"moves": 0,
"consistency_score": 0.0,
"average_probability": 0.0,
"blunders": 0,
"mistakes": 0,
"inaccuracies": 0,
"good": 0,
"excellent": 0,
"label": ""
},
"endgame": {
"moves": 0,
"consistency_score": 0.0,
"average_probability": 0.0,
"blunders": 0,
"mistakes": 0,
"inaccuracies": 0,
"good": 0,
"excellent": 0,
"label": ""
}
},
"moves": [
{
"move_number": 1,
"side": "white",
"uci": "e2e4",
"san": "e4",
"fen_before": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"fen_after": "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1",
"probability": 0.6400,
"rank": 1,
"classification": "excellent",
"eval_before": 0,
"eval_after": -18,
"eval_delta": -18,
"best_move": "e2e4",
"best_move_probability": 0.6400,
"is_capture": false,
"is_check": false,
"tactical_tags": [],
"phase": "opening"
}
],
"blunder_genealogy": [
{
"blunder_move": 4,
"root_cause_move": 2,
"explanation": "The blunder at move 4 was likely caused by difficulties that started around move 2. The position became complex after Bc4."
}
],
"missed_opportunities": [
{
"move_number": 3,
"side": "white",
"played_move": "Qh5",
"best_move": "Nf3",
"eval_delta_missed": 150,
"explanation": "Nf3 would have been much stronger, improving the position by 150 centipawns."
}
],
"danger_positions": [
{
"move_number": 3,
"side": "black",
"fen": "r1bqkbnr/pppp1ppp/2n5/4p3/2B1P3/8/PPPP1PPP/RNBQK1NR b KQkq - 1 3",
"danger_level": "moderate",
"danger_detail": "Position was complex with low probability for the played move.",
"player_response": "Nf6 (inaccuracy)",
"correct_response": "Qe7"
}
],
"eval_trajectory": [
{ "move": 1, "side": "white", "eval": -18 },
{ "move": 1, "side": "black", "eval": -5 },
{ "move": 2, "side": "white", "eval": -38 },
{ "move": 2, "side": "black", "eval": 52 },
{ "move": 3, "side": "white", "eval": -22 },
{ "move": 3, "side": "black", "eval": 493 },
{ "move": 4, "side": "white", "eval": 1000 }
],
"tactical_patterns": {
"missed_forks": 1,
"missed_pins": 0,
"missed_skewers": 0,
"missed_discovered_attacks": 0,
"hung_pieces": 0,
"walked_into_tactics": 0,
"most_common_mistake": "fork (1 occurrences)"
},
"recommendations": [
"Focus on opening technique — your consistency drops significantly in openings",
"Work on fork recognition — you missed opportunities to fork pieces",
"Review your opening repertoire — low consistency suggests unfamiliar positions"
]
}
```
### Field Reference
#### `summary`
| Field | Type | Description |
|-------|------|-------------|
| `consistency_score` | float | Average move probability across all moves (0–1) |
| `consistency_label` | string | Human-readable label: `"Excellent performance"`, `"Playing to your rating"`, `"Below your usual level"`, `"Significantly underperforming"` |
| `average_move_probability` | float | Same as consistency_score |
| `blunder_count` | int | Number of blunders (<5% probability) |
| `mistake_count` | int | Number of mistakes (5–15%) |
| `inaccuracy_count` | int | Number of inaccuracies (15–30%) |
| `good_count` | int | Number of good moves (30–60%) |
| `excellent_count` | int | Number of excellent moves (≥60%) |
| `missed_opportunities` | int | Positions where eval dropped >100cp and a better move existed |
#### `phase_breakdown`
Keys: `"opening"` (moves 1–10), `"middlegame"` (moves 11–30), `"endgame"` (move 31+ or ≤8 pieces).
Each phase has the same fields as `summary` plus a `label` describing performance.
#### `moves[]`
| Field | Type | Description |
|-------|------|-------------|
| `move_number` | int | Full move number |
| `side` | string | `"white"` or `"black"` |
| `uci` | string | Move in UCI format |
| `san` | string | Move in SAN |
| `fen_before` | string | FEN before the move |
| `fen_after` | string | FEN after the move |
| `probability` | float | Maia3's predicted probability |
| `rank` | int | Rank among legal moves |
| `classification` | string | Move quality |
| `eval_before` | int | Evaluation before the move (centipawns) |
| `eval_after` | int | Evaluation after the move (centipawns) |
| `eval_delta` | int | Change in evaluation |
| `best_move` | string | Maia3's top predicted move |
| `best_move_probability` | float | Probability of the best move |
| `is_capture` | bool | Whether the move is a capture |
| `is_check` | bool | Whether the move gives check |
| `tactical_tags` | string[] | Tactical motifs detected |
| `phase` | string | Game phase: `"opening"`, `"middlegame"`, `"endgame"` |
#### `blunder_genealogy[]`
Traces each blunder back to its root cause.
| Field | Type | Description |
|-------|------|-------------|
| `blunder_move` | int | Move number of the blunder |
| `root_cause_move` | int | Move number where difficulties began |
| `explanation` | string | Human-readable explanation |
#### `missed_opportunities[]`
| Field | Type | Description |
|-------|------|-------------|
| `move_number` | int | Move number |
| `side` | string | `"white"` or `"black"` |
| `played_move` | string | SAN of the played move |
| `best_move` | string | UCI of the better move |
| `eval_delta_missed` | int | Centipawns lost by not playing the best move |
| `explanation` | string | Human-readable explanation |
#### `danger_positions[]`
| Field | Type | Description |
|-------|------|-------------|
| `move_number` | int | Move number |
| `side` | string | `"white"` or `"black"` |
| `fen` | string | FEN of the dangerous position |
| `danger_level` | string | `"high"` or `"moderate"` |
| `danger_detail` | string | Description of the danger |
| `player_response` | string | What the player played (with classification) |
| `correct_response` | string | The better move |
#### `eval_trajectory[]`
| Field | Type | Description |
|-------|------|-------------|
| `move` | int | Move number |
| `side` | string | `"white"` or `"black"` |
| `eval` | int | Evaluation in centipawns after the move |
#### `tactical_patterns`
| Field | Type | Description |
|-------|------|-------------|
| `missed_forks` | int | Number of missed fork opportunities |
| `missed_pins` | int | Number of missed pins |
| `missed_skewers` | int | Number of missed skewers |
| `missed_discovered_attacks` | int | Number of missed discovered attacks |
| `hung_pieces` | int | Number of times a piece was hung |
| `walked_into_tactics` | int | Number of times the player walked into a tactic |
| `most_common_mistake` | string | Description of the most frequent mistake type |
#### `recommendations`
Array of strings with actionable improvement suggestions based on the analysis.
---
## Error Responses
All endpoints return errors in this format:
```json
{
"error": "Description of the error"
}
```
| Status Code | Meaning |
|-------------|---------|
| 400 | Bad request (invalid FEN, missing fields, invalid move) |
| 401 | Unauthorized (missing or invalid API key) |
| 405 | Method not allowed |
| 500 | Internal server error |
| 503 | Model unavailable (still loading or failed) |