Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1207,7 +1207,116 @@ def audio_transcription_tool(file_path: str) -> str:
|
|
| 1207 |
telemetry.record_call("audio_transcription_tool", time.time() - start_time, False)
|
| 1208 |
raise ToolError("audio_transcription_tool", e)
|
| 1209 |
|
|
|
|
|
|
|
|
|
|
| 1210 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1211 |
class ImageAnalysisInput(BaseModel):
|
| 1212 |
file_path: str = Field(description="Image file path")
|
| 1213 |
query: str = Field(description="What to analyze")
|
|
|
|
| 1207 |
telemetry.record_call("audio_transcription_tool", time.time() - start_time, False)
|
| 1208 |
raise ToolError("audio_transcription_tool", e)
|
| 1209 |
|
| 1210 |
+
class ChessAnalysisInput(BaseModel):
|
| 1211 |
+
image_path: str = Field(description="Path to chess board image")
|
| 1212 |
+
description: str = Field(description="Context about position", default="")
|
| 1213 |
|
| 1214 |
+
@tool(args_schema=ChessAnalysisInput)
|
| 1215 |
+
def analyze_chess_position(image_path: str, description: str = "") -> str:
|
| 1216 |
+
"""
|
| 1217 |
+
Analyze chess position using Stockfish.
|
| 1218 |
+
Requires stockfish binary installed.
|
| 1219 |
+
"""
|
| 1220 |
+
start_time = time.time()
|
| 1221 |
+
|
| 1222 |
+
try:
|
| 1223 |
+
print(f"♟️ Analyzing chess: {image_path}")
|
| 1224 |
+
|
| 1225 |
+
# Find image
|
| 1226 |
+
chess_image = find_file(image_path)
|
| 1227 |
+
if not chess_image and os.path.exists(image_path):
|
| 1228 |
+
chess_image = Path(image_path)
|
| 1229 |
+
|
| 1230 |
+
if not chess_image or not chess_image.exists():
|
| 1231 |
+
raise FileNotFoundError(f"Chess image not found: {image_path}")
|
| 1232 |
+
|
| 1233 |
+
# Extract FEN using Gemini Vision
|
| 1234 |
+
GOOGLE_API_KEY = os.getenv("GEMINI_API_KEY")
|
| 1235 |
+
if not GOOGLE_API_KEY:
|
| 1236 |
+
raise ValueError("GEMINI_API_KEY not set")
|
| 1237 |
+
|
| 1238 |
+
img = Image.open(chess_image)
|
| 1239 |
+
if img.mode not in ['RGB', 'RGBA']:
|
| 1240 |
+
img = img.convert('RGB')
|
| 1241 |
+
|
| 1242 |
+
buffered = io.BytesIO()
|
| 1243 |
+
img.save(buffered, format="JPEG")
|
| 1244 |
+
img_base64 = base64.b64encode(buffered.getvalue()).decode()
|
| 1245 |
+
|
| 1246 |
+
vision_llm = ChatGoogleGenerativeAI(
|
| 1247 |
+
model="gemini-2.5-flash",
|
| 1248 |
+
google_api_key=GOOGLE_API_KEY,
|
| 1249 |
+
temperature=0
|
| 1250 |
+
)
|
| 1251 |
+
|
| 1252 |
+
fen_prompt = """Analyze this chess board and provide FEN notation.
|
| 1253 |
+
Return ONLY the FEN string, nothing else.
|
| 1254 |
+
Format: piece_placement active_color castling en_passant halfmove fullmove"""
|
| 1255 |
+
|
| 1256 |
+
message = HumanMessage(
|
| 1257 |
+
content=[
|
| 1258 |
+
{"type": "text", "text": fen_prompt},
|
| 1259 |
+
{"type": "image_url", "image_url": f"data:image/jpeg;base64,{img_base64}"}
|
| 1260 |
+
]
|
| 1261 |
+
)
|
| 1262 |
+
|
| 1263 |
+
response = vision_llm.invoke([message])
|
| 1264 |
+
fen = response.content.strip()
|
| 1265 |
+
|
| 1266 |
+
# Clean FEN
|
| 1267 |
+
for line in fen.split('\n'):
|
| 1268 |
+
line = line.strip().replace('```', '').replace('fen', '')
|
| 1269 |
+
if '/' in line and ' ' in line:
|
| 1270 |
+
fen = line
|
| 1271 |
+
break
|
| 1272 |
+
|
| 1273 |
+
print(f"✓ FEN: {fen}")
|
| 1274 |
+
|
| 1275 |
+
# Analyze with Stockfish
|
| 1276 |
+
try:
|
| 1277 |
+
import chess
|
| 1278 |
+
from stockfish import Stockfish
|
| 1279 |
+
except ImportError:
|
| 1280 |
+
raise ImportError("Need: pip install python-chess stockfish")
|
| 1281 |
+
|
| 1282 |
+
# Find Stockfish binary
|
| 1283 |
+
stockfish_paths = [
|
| 1284 |
+
"/usr/games/stockfish",
|
| 1285 |
+
"/usr/local/bin/stockfish",
|
| 1286 |
+
"/usr/bin/stockfish",
|
| 1287 |
+
"stockfish"
|
| 1288 |
+
]
|
| 1289 |
+
|
| 1290 |
+
stockfish_path = None
|
| 1291 |
+
for path in stockfish_paths:
|
| 1292 |
+
if os.path.exists(path):
|
| 1293 |
+
stockfish_path = path
|
| 1294 |
+
break
|
| 1295 |
+
|
| 1296 |
+
if not stockfish_path:
|
| 1297 |
+
raise FileNotFoundError("Stockfish binary not found. Install: apt-get install stockfish")
|
| 1298 |
+
|
| 1299 |
+
stockfish = Stockfish(path=stockfish_path, depth=20)
|
| 1300 |
+
stockfish.set_fen_position(fen)
|
| 1301 |
+
|
| 1302 |
+
best_move_uci = stockfish.get_best_move()
|
| 1303 |
+
if not best_move_uci:
|
| 1304 |
+
raise ValueError("No legal move found")
|
| 1305 |
+
|
| 1306 |
+
# Convert to SAN
|
| 1307 |
+
board = chess.Board(fen)
|
| 1308 |
+
uci_move = chess.Move.from_uci(best_move_uci)
|
| 1309 |
+
san_move = board.san(uci_move)
|
| 1310 |
+
|
| 1311 |
+
print(f"✓ Best move: {san_move}")
|
| 1312 |
+
|
| 1313 |
+
telemetry.record_call("analyze_chess_position", time.time() - start_time, True)
|
| 1314 |
+
return san_move
|
| 1315 |
+
|
| 1316 |
+
except Exception as e:
|
| 1317 |
+
telemetry.record_call("analyze_chess_position", time.time() - start_time, False)
|
| 1318 |
+
raise ToolError("analyze_chess_position", e, "Check if stockfish installed")
|
| 1319 |
+
|
| 1320 |
class ImageAnalysisInput(BaseModel):
|
| 1321 |
file_path: str = Field(description="Image file path")
|
| 1322 |
query: str = Field(description="What to analyze")
|