Spaces:
Running
Running
Download openapi.yaml from chesstb/explorer: direct link, hf CLI and curl.
- Browser
- Download file 20.9 kB
-
https://huggingface.co/spaces/chesstb/explorer/resolve/main/openapi.yaml
- Command line
-
hf download hf://spaces/chesstb/explorer/openapi.yaml
-
curl -L -o openapi.yaml https://huggingface.co/spaces/chesstb/explorer/resolve/main/openapi.yaml
20.9 kB
| openapi: "3.0.3" | |
| info: | |
| title: ChessTB Explorer API | |
| version: "1.0.0" | |
| description: | | |
| Chess endgame tablebase probe API. | |
| Submit a FEN position and receive ranked move lists for Distance to Zeroing (DTZ), | |
| Distance to Conversion (DTC), Distance to Mate (DTM), and DTM under the 50-move rule (DTM50). | |
| paths: | |
| /probe: | |
| post: | |
| operationId: probePosition | |
| summary: Probe a chess position (buffered response) | |
| parameters: | |
| - $ref: "#/components/parameters/XPrefetch" | |
| requestBody: | |
| required: true | |
| content: | |
| application/json: | |
| schema: | |
| $ref: "#/components/schemas/ProbeRequest" | |
| example: | |
| fen: "4k3/8/8/8/8/8/8/4K2R w - - 0 1" | |
| responses: | |
| "200": | |
| description: Successful probe | |
| content: | |
| application/json: | |
| schema: | |
| $ref: "#/components/schemas/ProbeResponse" | |
| "400": | |
| description: Invalid FEN, empty body, or missing tablebase coverage | |
| content: | |
| application/json: | |
| schema: | |
| $ref: "#/components/schemas/ErrorResponse" | |
| "413": | |
| description: Request body exceeds 4 KB limit | |
| "429": | |
| description: >- | |
| Rate limit exceeded -- PROBE_RATE_LIMIT per client IP, or the | |
| separate, tighter PREFETCH_RATE_LIMIT when the request carries | |
| "X-Prefetch: 1" (see that parameter above). Retrying after a | |
| short wait should succeed. | |
| content: | |
| application/json: | |
| schema: | |
| $ref: "#/components/schemas/ErrorResponse" | |
| "502": | |
| description: >- | |
| Remote (URL) tablebase entries only: the tablebase's HTTP | |
| source couldn't be reached for this probe. Retrying may | |
| succeed. | |
| content: | |
| application/json: | |
| schema: | |
| $ref: "#/components/schemas/ErrorResponse" | |
| "503": | |
| description: >- | |
| Two distinct causes, both with this status code: (1) tablebase | |
| not initialised (TABLEBASE_PATH failed to open) - no | |
| error_code is set on the response body in this case; (2) an | |
| in-flight probe of this (variant, FEN) pair by another | |
| concurrent request didn't finish within PROBE_TIMEOUT_SECS | |
| (error_code=probe_timeout). Retrying may succeed for case (2). | |
| content: | |
| application/json: | |
| schema: | |
| $ref: "#/components/schemas/ErrorResponse" | |
| "500": | |
| description: Internal server error | |
| /probe/stream: | |
| post: | |
| operationId: probePositionStream | |
| summary: Probe a chess position with SSE streaming progress | |
| description: | | |
| Identical to /probe but returns text/event-stream. | |
| Each SSE event has a JSON payload in its data field. | |
| Events are emitted as child positions are probed, followed by the | |
| final result or an error event. An error event with | |
| error_code=probe_timeout means a concurrent request was already | |
| probing the same (variant, FEN) pair and didn't finish in time; | |
| retrying is usually cheap and likely to resolve. | |
| parameters: | |
| - $ref: "#/components/parameters/XPrefetch" | |
| requestBody: | |
| required: true | |
| content: | |
| application/json: | |
| schema: | |
| $ref: "#/components/schemas/ProbeRequest" | |
| responses: | |
| "200": | |
| description: SSE stream | |
| content: | |
| text/event-stream: | |
| schema: | |
| oneOf: | |
| - $ref: "#/components/schemas/ProgressEvent" | |
| - $ref: "#/components/schemas/DoneEvent" | |
| - $ref: "#/components/schemas/ErrorEvent" | |
| "400": | |
| description: >- | |
| Invalid request body, returned before the stream opens: missing | |
| or non-string FEN, empty FEN, FEN over 100 characters, or | |
| unsupported variant. An invalid board or a position outside | |
| tablebase coverage is reported as an error event on the stream. | |
| content: | |
| application/json: | |
| schema: | |
| $ref: "#/components/schemas/ErrorResponse" | |
| "429": | |
| description: >- | |
| Rate limit exceeded, returned before the stream opens -- | |
| PROBE_RATE_LIMIT per client IP, or the separate, tighter | |
| PREFETCH_RATE_LIMIT when the request carries "X-Prefetch: 1" | |
| (see that parameter above; this is the endpoint the | |
| frontend's hover-prefetch actually sets it on). Retrying | |
| after a short wait should succeed. | |
| content: | |
| application/json: | |
| schema: | |
| $ref: "#/components/schemas/ErrorResponse" | |
| /health: | |
| get: | |
| operationId: healthCheck | |
| summary: Tablebase readiness check | |
| responses: | |
| "200": | |
| description: Tablebase loaded and ready | |
| "503": | |
| description: Tablebase not initialised | |
| /admin/cache/stats: | |
| get: | |
| operationId: cacheStats | |
| summary: LRU cache hit-rate statistics | |
| security: | |
| - adminBearer: [] | |
| - adminSession: [] | |
| responses: | |
| "200": | |
| description: Cache statistics | |
| content: | |
| application/json: | |
| schema: | |
| $ref: "#/components/schemas/CacheStatsResponse" | |
| "401": | |
| description: Admin authentication required | |
| "503": | |
| description: Admin panel disabled (ADMIN_TOKEN not configured) | |
| /admin/cache/clear: | |
| post: | |
| operationId: cacheClear | |
| summary: Purge both in-process LRU caches, and the tablebase's own block cache if supported | |
| security: | |
| - adminBearer: [] | |
| - adminSession: [] | |
| responses: | |
| "200": | |
| description: Caches cleared | |
| content: | |
| application/json: | |
| schema: | |
| $ref: "#/components/schemas/CacheClearResponse" | |
| "401": | |
| description: Admin authentication required | |
| "503": | |
| description: Admin panel disabled (ADMIN_TOKEN not configured) | |
| /admin/login: | |
| post: | |
| operationId: adminLogin | |
| summary: Start an admin session | |
| requestBody: | |
| required: true | |
| content: | |
| application/json: | |
| schema: | |
| type: object | |
| required: [token] | |
| properties: | |
| token: | |
| type: string | |
| description: Value of ADMIN_TOKEN (config.py's "Admin" section) | |
| responses: | |
| "200": | |
| description: Session started; response sets the admin session cookie | |
| "401": | |
| description: Incorrect token | |
| "429": | |
| description: >- | |
| Rate limit exceeded (ADMIN_LOGIN_RATE_LIMIT, per client IP). | |
| Retrying after a short wait should succeed. | |
| "503": | |
| description: Admin panel disabled (ADMIN_TOKEN not configured) | |
| /admin/logout: | |
| post: | |
| operationId: adminLogout | |
| summary: End the admin session | |
| responses: | |
| "200": | |
| description: Session ended | |
| components: | |
| securitySchemes: | |
| adminBearer: | |
| type: http | |
| scheme: bearer | |
| description: "Authorization: Bearer <ADMIN_TOKEN>" | |
| adminSession: | |
| type: apiKey | |
| in: cookie | |
| name: session | |
| description: Session cookie set by a successful POST /admin/login | |
| parameters: | |
| XPrefetch: | |
| name: X-Prefetch | |
| in: header | |
| required: false | |
| schema: | |
| type: string | |
| enum: ["1"] | |
| description: >- | |
| Set to "1" by the frontend's hover-prefetch (frontend/src/engine/tablebase.js) | |
| to route this request to PREFETCH_RATE_LIMIT instead of | |
| PROBE_RATE_LIMIT -- a separate, tighter per-client-IP budget, never | |
| an exemption from rate limiting (see app.py's _request_probe_limit). | |
| A real, user-initiated probe should not set this header. | |
| schemas: | |
| ProbeRequest: | |
| type: object | |
| required: [fen] | |
| properties: | |
| fen: | |
| type: string | |
| description: >- | |
| FEN notation (4–6 fields; shorter forms are auto-padded). The | |
| castling-availability field accepts standard KQkq letters (in | |
| that order) for variant="standard", or additionally | |
| Shredder-FEN/X-FEN rook-file letters (e.g. "A", "Ha") for | |
| variant="chess960". Either way, a letter naming a right with | |
| no matching king and rook on the board is rejected with a 400 | |
| response. A right that does have one is accepted and probed | |
| (see TABLEBASE_PATH in the project README). A FEN | |
| longer than 100 characters is rejected with a 400 response. | |
| example: "4k3/8/8/8/8/8/8/4K2R w - - 0 1" | |
| variant: | |
| type: string | |
| enum: ["standard", "chess960"] | |
| default: "standard" | |
| description: >- | |
| The rules variant "fen" is interpreted under. Never inferred | |
| from the FEN itself - the orthodox starting array is itself a | |
| legal Chess960 starting position, so the same FEN text is | |
| genuinely ambiguous without this field. Omitted or absent | |
| defaults to "standard". Board geometry, | |
| piece movement, and tablebase material selection are | |
| identical between the two variants - only the interpretation | |
| of castling rights/moves differs. | |
| example: "standard" | |
| MoveEntry: | |
| type: object | |
| properties: | |
| san: | |
| type: string | |
| description: Standard Algebraic Notation of the move | |
| plies: | |
| type: integer | |
| description: >- | |
| Distance in plies. 1 for a move that delivers immediate | |
| checkmate (the move itself is the mate); 0 for a move that | |
| ends the game immediately as a draw (stalemate or insufficient | |
| material), for an unprobed/unknown move, or when available is | |
| false. | |
| order: | |
| type: integer | |
| description: >- | |
| DTC only: pawn pushes still owed before this move's line | |
| reaches its next capture or promotion (see chess.chesstb's | |
| probe_dtc/ProbeResult docs). Always 0 for dtz/dtm/dtm50 | |
| entries. 0 for a DTC entry when the move itself is a capture | |
| or a promotion, or when it ends the game outright (checkmate | |
| or an immediate draw) - nothing more is owed once the game is | |
| decided or the conversion has already happened. A quiet pawn | |
| push does not settle the count: it only pays down one of the | |
| pushes still owed. | |
| is_mate: | |
| type: boolean | |
| description: True if this move delivers immediate checkmate | |
| outcome: | |
| type: string | |
| enum: [win, cursed_win, draw, blessed_loss, loss, unknown, not_available] | |
| available: | |
| type: boolean | |
| description: >- | |
| False when this material's tablebase has no table for this | |
| entry's metric (DTZ, DTC, DTM, or DTM50 - WDL is always | |
| available), in which case outcome is flatly not_available and | |
| plies/order are 0. No metric's row ever derives its answer | |
| from another metric. | |
| child_fen: | |
| type: string | |
| nullable: true | |
| description: FEN of the position after this move; null for terminal positions | |
| draw_reason: | |
| type: string | |
| nullable: true | |
| enum: [stalemate, insufficient_material, null] | |
| description: Reason for a draw outcome that ends the game immediately; null otherwise | |
| rounded: | |
| type: boolean | |
| description: >- | |
| Present on available moves_dtz entries only. True when plies may | |
| still be one ply off after cross-checking the moves against the | |
| position's dtz. False for zeroing moves, whose plies is exactly 1, | |
| and for moves whose value the cross-check determined. | |
| Summary: | |
| type: object | |
| description: >- | |
| Outcome distribution across all legal moves under the 50-move rule, | |
| using each resulting position's halfmove clock. Outcomes use DTM50 | |
| when available, otherwise DTC (including DTZ-derived DTC for pawnless | |
| material). Clock-independent draws, cursed wins and blessed losses | |
| count as draws. A move whose clock-aware outcome cannot be resolved | |
| counts as unknown, including failed child probes. | |
| properties: | |
| wins: {type: integer} | |
| draws: {type: integer} | |
| losses: {type: integer} | |
| unknown: {type: integer} | |
| ProbeResponse: | |
| type: object | |
| properties: | |
| wdl: | |
| type: integer | |
| enum: [-2, -1, 0, 1, 2] | |
| description: Root position WDL (2=win, 1=cursed win, 0=draw, -1=blessed loss, -2=loss) | |
| dtz: | |
| type: integer | |
| nullable: true | |
| description: >- | |
| Distance to Zeroing (DTZ) in plies; null when | |
| dtz_available is false | |
| dtz_available: | |
| type: boolean | |
| description: False when this material's tablebase has no DTZ table | |
| dtz_rounded: | |
| type: boolean | |
| nullable: true | |
| description: >- | |
| True when dtz may still be one ply off after the moves have been | |
| cross-checked against it. Only cursed wins and blessed losses can | |
| be rounded. Null when dtz_available is false. | |
| dtc: | |
| type: array | |
| items: | |
| type: integer | |
| nullable: true | |
| minItems: 3 | |
| maxItems: 3 | |
| description: >- | |
| [wdl_under_dtc, pawn_pushes_still_owed, plies_to_next_conversion]: | |
| Distance to Conversion (DTC), the chess.chesstb probe_dtc() pair | |
| (pawn pushes still owed, signed plies) preceded by the wdl | |
| class; [null, null, null] when dtc_available is false. DTC is | |
| always rule50-aware (probed at the position's own halfmove | |
| clock, like DTM50), so wdl_under_dtc can differ from the root | |
| wdl above the same way dtm50's own wdl can. | |
| dtc_available: | |
| type: boolean | |
| description: >- | |
| False when this material's tablebase has no DTC table. A | |
| .lzdtc table only exists for material with at least one pawn; | |
| chess.chesstb derives an equivalent DTC answer from DTZ/WDL for | |
| pawnless material instead, so this is False in practice only | |
| when a pawnful material's own DTC table hasn't been generated. | |
| dtm: | |
| type: integer | |
| nullable: true | |
| description: Distance to Mate in plies; null when dtm_available is false | |
| dtm_available: | |
| type: boolean | |
| description: False when this material's tablebase has no DTM table | |
| dtm50: | |
| type: array | |
| items: | |
| type: integer | |
| nullable: true | |
| minItems: 2 | |
| maxItems: 2 | |
| description: >- | |
| [wdl_under_50move_rule, plies_to_mate_under_50move_rule]; | |
| [null, null] when dtm50_available is false | |
| dtm50_available: | |
| type: boolean | |
| description: False when this material's tablebase has no DTM50 table | |
| draw_reason: | |
| type: string | |
| nullable: true | |
| enum: [stalemate, insufficient_material, null] | |
| description: Reason the root position itself is an immediate draw; null otherwise | |
| moves_dtz: | |
| type: array | |
| items: | |
| $ref: "#/components/schemas/MoveEntry" | |
| description: >- | |
| All legal moves ranked by Distance to Zeroing (best first). A | |
| quiet move that gives up castling rights into a cursed win or | |
| blessed loss takes its plies from chess.chesstb's cursed-exit | |
| rule: one or two more than the resulting position's dtz. A | |
| rounded value is exact whenever the position's dtz and the other | |
| moves leave a single possibility. | |
| moves_dtc: | |
| type: array | |
| items: | |
| $ref: "#/components/schemas/MoveEntry" | |
| description: >- | |
| All legal moves ranked by Distance to Conversion (best first); | |
| ranked by pawn pushes still owed first, then plies (DTC's own | |
| notion of a better line - see MoveEntry.order) | |
| moves_dtm: | |
| type: array | |
| items: | |
| $ref: "#/components/schemas/MoveEntry" | |
| description: All legal moves ranked by Distance to Mate (best first) | |
| moves_dtm50: | |
| type: array | |
| items: | |
| $ref: "#/components/schemas/MoveEntry" | |
| description: All legal moves ranked by DTM under the 50-move rule (best first) | |
| summary: | |
| $ref: "#/components/schemas/Summary" | |
| ErrorResponse: | |
| type: object | |
| properties: | |
| error: | |
| type: string | |
| description: Human-readable error message | |
| error_code: | |
| type: string | |
| enum: [missing_table, remote_unavailable, probe_timeout] | |
| description: >- | |
| Machine-readable error code (only present for specific errors). | |
| remote_unavailable only occurs with a remote (URL) | |
| TABLEBASE_PATH, when its HTTP source couldn't be reached. | |
| probe_timeout means a concurrent request was already probing | |
| this same FEN and didn't finish within PROBE_TIMEOUT_SECS; | |
| retrying is generally cheap and likely to succeed, since the | |
| original probe has usually finished and been cached by then. | |
| piece_count: | |
| type: integer | |
| nullable: true | |
| description: Number of pieces on the board (only when error_code=missing_table) | |
| ProgressEvent: | |
| type: object | |
| properties: | |
| status: {type: string, enum: [probing]} | |
| completed: {type: integer} | |
| total: {type: integer} | |
| DoneEvent: | |
| allOf: | |
| - $ref: "#/components/schemas/ProbeResponse" | |
| - type: object | |
| properties: | |
| status: {type: string, enum: [done]} | |
| ErrorEvent: | |
| type: object | |
| properties: | |
| status: {type: string, enum: [error]} | |
| error: {type: string} | |
| error_code: {type: string, enum: [missing_table, remote_unavailable, probe_timeout]} | |
| piece_count: {type: integer, nullable: true} | |
| CacheStatsResponse: | |
| type: object | |
| description: >- | |
| Point-in-time hit-rate counters for both in-process LRUs, plus the | |
| static config and tablebase tier info they're sized against. | |
| properties: | |
| evaluate_fen_cache: | |
| $ref: "#/components/schemas/CacheCounters" | |
| probe_fen_cache: | |
| $ref: "#/components/schemas/CacheCounters" | |
| config: | |
| type: object | |
| properties: | |
| evaluate_cache_size: {type: integer} | |
| probe_cache_size: {type: integer} | |
| block_cache_bytes: {type: integer} | |
| tablebase: | |
| type: object | |
| description: Whether the ordered source resolver is configured. | |
| properties: | |
| configured: {type: boolean} | |
| thread_pool: | |
| type: object | |
| properties: | |
| max_workers: {type: integer} | |
| parallel_threshold: {type: integer} | |
| probe_timeout_secs: {type: number} | |
| tablebase_cache: | |
| type: object | |
| description: >- | |
| Per-source cache statistics: block_cache plus one entry | |
| named source containing the resolver's own cache statistics. | |
| Omitted when nothing reports statistics. | |
| additionalProperties: true | |
| CacheCounters: | |
| type: object | |
| properties: | |
| currsize: {type: integer} | |
| maxsize: {type: integer} | |
| hits: {type: integer} | |
| misses: {type: integer} | |
| hit_rate: {type: number} | |
| CacheClearResponse: | |
| type: object | |
| properties: | |
| status: {type: string, enum: [ok]} | |
| message: {type: string} | |
| evaluate_fen_cache: | |
| $ref: "#/components/schemas/CacheCounters" | |
| probe_fen_cache: | |
| $ref: "#/components/schemas/CacheCounters" | |
| tablebase_internal_cache_cleared: | |
| type: boolean | |
| description: >- | |
| Whether the underlying chess.chesstb block cache was also | |
| cleared, not just this app's own two LRUs above. | |
| tablebase_cache: | |
| type: object | |
| description: >- | |
| Per-source cache statistics: block_cache plus one entry | |
| named source containing the resolver's own cache statistics. | |
| Omitted when nothing reports statistics. | |
| additionalProperties: true | |