-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #125 from dcSpark/feature/add-chess-generate-image…
…-tool feat: add chess-generate-image tool
- Loading branch information
Showing
6 changed files
with
181 additions
and
0 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { expect } from '@jest/globals'; | ||
import { getToolTestClient } from '../../src/test/utils'; | ||
import * as path from 'path'; | ||
import * as fs from 'fs'; | ||
|
||
describe('Chess Generate Image Tool', () => { | ||
const toolPath = path.join(__dirname, 'tool.py'); | ||
const client = getToolTestClient(); | ||
|
||
it('generates an image for the starting position', async () => { | ||
const response = await client.executeToolFromFile(toolPath, { | ||
fen: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", | ||
output_filename: "test_starting_position.png" | ||
}); | ||
|
||
console.log(response); | ||
|
||
expect(response).toHaveProperty('image_path'); | ||
expect(typeof response.image_path).toBe('string'); | ||
expect(response.image_path).toContain('test_starting_position.png'); | ||
expect(fs.existsSync(response.image_path)).toBe(true); | ||
|
||
// Check if file is a valid PNG | ||
const fileBuffer = fs.readFileSync(response.image_path); | ||
expect(fileBuffer.length).toBeGreaterThan(0); | ||
expect(fileBuffer.toString('hex', 0, 8)).toBe('89504e470d0a1a0a'); // PNG magic number | ||
}, 20000); | ||
|
||
it('generates an image with highlighted last move', async () => { | ||
const response = await client.executeToolFromFile(toolPath, { | ||
fen: "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1", | ||
last_move_uci: "e2e4", | ||
output_filename: "test_with_highlight.png" | ||
}); | ||
|
||
console.log(response); | ||
|
||
expect(response).toHaveProperty('image_path'); | ||
expect(typeof response.image_path).toBe('string'); | ||
expect(response.image_path).toContain('test_with_highlight.png'); | ||
expect(fs.existsSync(response.image_path)).toBe(true); | ||
|
||
const fileBuffer = fs.readFileSync(response.image_path); | ||
expect(fileBuffer.length).toBeGreaterThan(0); | ||
expect(fileBuffer.toString('hex', 0, 8)).toBe('89504e470d0a1a0a'); | ||
}, 20000); | ||
|
||
it('handles invalid FEN', async () => { | ||
await expect(client.executeToolFromFile(toolPath, { | ||
fen: "not-a-valid-FEN" | ||
})).rejects.toThrow(/Invalid FEN/); | ||
}, 10000); | ||
|
||
it('handles invalid last_move_uci', async () => { | ||
const response = await client.executeToolFromFile(toolPath, { | ||
fen: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", | ||
last_move_uci: "invalid-move", | ||
output_filename: "test_invalid_move.png" | ||
}); | ||
|
||
expect(response).toHaveProperty('image_path'); | ||
expect(typeof response.image_path).toBe('string'); | ||
expect(response.image_path).toContain('test_invalid_move.png'); | ||
expect(fs.existsSync(response.image_path)).toBe(true); | ||
|
||
const fileBuffer = fs.readFileSync(response.image_path); | ||
expect(fileBuffer.length).toBeGreaterThan(0); | ||
expect(fileBuffer.toString('hex', 0, 8)).toBe('89504e470d0a1a0a'); | ||
}, 20000); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
{ | ||
"id": "chess-generate-image", | ||
"name": "Chess Generate Image", | ||
"version": "1.0.0", | ||
"description": "Generate a PNG image of a chess position from FEN notation with optional last move highlighting", | ||
"author": "Example", | ||
"keywords": [ | ||
"chess", | ||
"image", | ||
"png", | ||
"fen", | ||
"board", | ||
"visualization" | ||
], | ||
"configurations": { | ||
"type": "object", | ||
"properties": {}, | ||
"required": [] | ||
}, | ||
"parameters": { | ||
"type": "object", | ||
"properties": { | ||
"fen": { | ||
"type": "string", | ||
"description": "FEN string representing the chess position" | ||
}, | ||
"last_move_uci": { | ||
"type": "string", | ||
"description": "Optional UCI format move to highlight (e.g. 'e2e4')" | ||
}, | ||
"output_filename": { | ||
"type": "string", | ||
"description": "Optional filename for the output PNG image (default: chess_position.png)", | ||
"default": "chess_position.png" | ||
} | ||
}, | ||
"required": ["fen"] | ||
}, | ||
"result": { | ||
"type": "object", | ||
"properties": { | ||
"image_path": { | ||
"type": "string", | ||
"description": "Path to the generated PNG image file" | ||
} | ||
}, | ||
"required": ["image_path"] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"categoryId": "f4906ba5-16bb-445d-9241-85422cf5a055" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# /// script | ||
# dependencies = [ | ||
# "python-chess>=1.999", | ||
# "cairosvg>=2.5.2", | ||
# "requests" | ||
# ] | ||
# /// | ||
import chess | ||
import chess.svg | ||
import cairosvg | ||
import os | ||
from typing import Dict, Any, Optional, List | ||
from shinkai_local_support import get_home_path | ||
|
||
class CONFIG: | ||
pass | ||
|
||
class INPUTS: | ||
fen: str | ||
last_move_uci: Optional[str] = None # highlight the last move, optional | ||
output_filename: str = "chess_position.png" # allow customizing the output filename | ||
|
||
class OUTPUT: | ||
image_path: str | ||
|
||
async def run(c: CONFIG, p: INPUTS) -> OUTPUT: | ||
if not p.fen: | ||
raise ValueError("No FEN provided") | ||
|
||
board = chess.Board() | ||
try: | ||
board.set_fen(p.fen) | ||
except ValueError: | ||
raise ValueError("Invalid FEN") | ||
|
||
# Optionally highlight the last move | ||
arrows = [] | ||
if p.last_move_uci and len(p.last_move_uci) in (4,5): | ||
try: | ||
move = board.parse_uci(p.last_move_uci) | ||
arrows.append(chess.svg.Arrow(start=move.from_square, end=move.to_square, color="#FF0000")) | ||
except: | ||
pass | ||
|
||
svg_data = chess.svg.board(board, arrows=arrows) | ||
png_data = cairosvg.svg2png(bytestring=svg_data.encode("utf-8")) | ||
|
||
# Get home path and create output path | ||
home_path = await get_home_path() | ||
filename = p.output_filename | ||
file_path = os.path.join(home_path, filename) | ||
|
||
# Write the PNG data to file | ||
with open(file_path, "wb") as f: | ||
f.write(png_data) | ||
|
||
out = OUTPUT() | ||
out.image_path = file_path | ||
return out |