Lyte commited on
Commit
eb3896f
·
verified ·
1 Parent(s): 0424e7f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +156 -100
app.py CHANGED
@@ -4,6 +4,7 @@ from llama_cpp import Llama
4
  from huggingface_hub import hf_hub_download
5
  import numpy as np
6
  from typing import List
 
7
 
8
  model = Llama(
9
  model_path=hf_hub_download(
@@ -47,6 +48,16 @@ def extract_xml_move(text: str) -> str:
47
  return match.group(1)
48
  return ""
49
 
 
 
 
 
 
 
 
 
 
 
50
  def convert_moves_to_coordinate_list(moves_list: List[str]) -> str:
51
  """
52
  Converts a list of moves to a coordinate list representation.
@@ -291,6 +302,9 @@ def create_interface():
291
  margin: 15px 0;
292
  font-family: monospace;
293
  min-height: 100px;
 
 
 
294
  }
295
  .reasoning-box {
296
  border-left: 4px solid #2196F3;
@@ -313,6 +327,10 @@ def create_interface():
313
  div.svelte-1nguped {
314
  display: block;
315
  }
 
 
 
 
316
  """
317
 
318
  with gr.Blocks(css=css) as interface:
@@ -352,7 +370,7 @@ def create_interface():
352
  info="Lower values make AI more deterministic, higher values more creative"
353
  )
354
 
355
- def handle_move(col, temperature=0.8):
356
  if game.game_over:
357
  return [
358
  render_board(game.board),
@@ -386,114 +404,145 @@ def create_interface():
386
  game_state = game.format_game_state()
387
  print(game_state)
388
 
389
- # Create streaming response generator
390
- def stream_response():
391
- # Initialize streaming response
392
- reasoning_html = '<div id="ai-reasoning"><div class="reasoning-box"><p><strong>🤔 Reasoning:</strong></p><p>'
393
-
394
- # Initialize variables to store the full response
395
- full_response = ""
396
- reasoning_content = ""
397
- move_str = ""
398
-
399
- # Set up streaming
400
- response_stream = model.create_chat_completion(
401
- messages=[
402
- {"role": "system", "content": SYSTEM_PROMPT},
403
- {"role": "user", "content": game_state}
404
- ],
405
- temperature=temperature,
406
- top_p=0.95,
407
- max_tokens=1024,
408
- stream=True
409
- )
410
-
411
- # Process stream chunks
412
- for chunk in response_stream:
413
- if 'choices' in chunk and len(chunk['choices']) > 0:
414
- content = chunk['choices'][0]['delta'].get('content', '')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
415
  if content:
 
416
  full_response += content
417
 
418
- # Try to extract reasoning as it comes
419
- if "<reasoning>" in full_response and "</reasoning>" in full_response:
420
- parts = full_response.split("<reasoning>")
 
421
  if len(parts) > 1:
422
- reasoning_parts = parts[1].split("</reasoning>")
423
- if len(reasoning_parts) > 0:
424
- reasoning_content = reasoning_parts[0].strip()
425
-
426
- # Update the HTML with the current reasoning content
427
- current_reasoning_html = reasoning_html + reasoning_content + '</p>'
428
-
429
- # Try to extract move as it comes
430
- temp_move_str = extract_xml_move(full_response)
431
- if temp_move_str:
432
- move_str = temp_move_str
433
- current_reasoning_html += f'<p><strong>📍 Move chosen:</strong> Column {move_str.upper()}</p>'
434
 
435
- current_reasoning_html += '</div></div>'
 
 
436
 
437
- # Yield the updated HTML
438
- yield [
439
- render_board(game.board),
440
- "AI is thinking...",
441
- current_reasoning_html
442
- ]
443
-
444
- # Final response with complete reasoning and move
445
- if not reasoning_content:
446
- try:
447
- reasoning_content = full_response.split("<reasoning>")[1].split("</reasoning>")[0].strip()
448
- except:
449
- reasoning_content = "AI couldn't provide clear reasoning."
450
-
451
- if not move_str:
452
- move_str = extract_xml_move(full_response)
453
-
454
- final_reasoning_html = f'''
455
- <div id="ai-reasoning">
456
- <div class="reasoning-box">
457
- <p><strong>🤔 Reasoning:</strong></p>
458
- <p>{reasoning_content}</p>
459
- <p><strong>📍 Move chosen:</strong> Column {move_str.upper() if move_str else "?"}</p>
460
- </div>
461
- </div>
462
- '''
463
-
464
- # Make AI move
465
- if move_str:
466
- ai_col = game.parse_ai_move(move_str)
467
-
468
- if ai_col != -1:
469
- success, _ = game.make_move(ai_col)
470
- if success:
471
- # Check for AI winner
472
- winner = game.check_winner()
473
- if winner == 2:
474
- game.game_over = True
475
  yield [
476
  render_board(game.board),
477
- "🤖 AI wins! Better luck next time!",
478
- final_reasoning_html
479
  ]
480
- return
481
- else:
482
- # AI made a valid move but didn't win
483
- game.current_player = 1
484
- yield [render_board(game.board), "Your turn!", final_reasoning_html]
485
- return
 
 
 
 
 
 
 
 
 
486
 
487
- # If we get here, something went wrong with the AI move
488
- game.game_over = True
489
- yield [
490
- render_board(game.board),
491
- "AI made an invalid move! You win by default!",
492
- '<div id="ai-reasoning">AI made an invalid move or provided an invalid response!</div>'
493
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
494
 
495
- # Return the streaming generator
496
- return stream_response()
 
 
 
 
 
497
 
498
  def reset_game():
499
  game.board = np.zeros((6, 7))
@@ -509,13 +558,20 @@ def create_interface():
509
 
510
  # Event handlers
511
  for i, btn in enumerate(col_buttons):
 
512
  btn.click(
513
- fn=handle_move,
514
  inputs=[
515
  gr.Number(value=i, visible=False),
516
  temperature_slider
517
  ],
518
- outputs=[board_display, status, reasoning_display]
 
 
 
 
 
 
519
  )
520
 
521
  reset_btn.click(
 
4
  from huggingface_hub import hf_hub_download
5
  import numpy as np
6
  from typing import List
7
+ import time
8
 
9
  model = Llama(
10
  model_path=hf_hub_download(
 
48
  return match.group(1)
49
  return ""
50
 
51
+ def extract_reasoning(text: str) -> str:
52
+ """
53
+ Extract the reasoning part from the XML response.
54
+ """
55
+ import re
56
+ match = re.search(r'<reasoning>(.*?)</reasoning>', text, re.DOTALL)
57
+ if match:
58
+ return match.group(1).strip()
59
+ return ""
60
+
61
  def convert_moves_to_coordinate_list(moves_list: List[str]) -> str:
62
  """
63
  Converts a list of moves to a coordinate list representation.
 
302
  margin: 15px 0;
303
  font-family: monospace;
304
  min-height: 100px;
305
+ max-height: 400px;
306
+ overflow-y: auto;
307
+ white-space: pre-wrap;
308
  }
309
  .reasoning-box {
310
  border-left: 4px solid #2196F3;
 
327
  div.svelte-1nguped {
328
  display: block;
329
  }
330
+ .thinking-indicator {
331
+ color: #ffc107;
332
+ font-style: italic;
333
+ }
334
  """
335
 
336
  with gr.Blocks(css=css) as interface:
 
370
  info="Lower values make AI more deterministic, higher values more creative"
371
  )
372
 
373
+ def handle_player_move(col, temperature=0.8):
374
  if game.game_over:
375
  return [
376
  render_board(game.board),
 
404
  game_state = game.format_game_state()
405
  print(game_state)
406
 
407
+ # Prepare for AI's turn
408
+ reasoning_html = '''
409
+ <div id="ai-reasoning">
410
+ <div class="reasoning-box">
411
+ <p><strong>🤔 Reasoning:</strong></p>
412
+ <p class="thinking-indicator">AI is thinking...</p>
413
+ </div>
414
+ </div>
415
+ '''
416
+
417
+ return [
418
+ render_board(game.board),
419
+ "AI is thinking...",
420
+ reasoning_html
421
+ ]
422
+
423
+ def process_ai_response(temperature=0.8):
424
+ if game.game_over or game.current_player != 2:
425
+ return [
426
+ render_board(game.board),
427
+ "Your turn!" if not game.game_over else "Game is over! Click New Game to play again.",
428
+ reasoning_display.value
429
+ ]
430
+
431
+ game_state = game.format_game_state()
432
+
433
+ # Start streaming response
434
+ full_response = ""
435
+ reasoning_content = ""
436
+ move_str = ""
437
+
438
+ # Begin HTML
439
+ reasoning_html = '''
440
+ <div id="ai-reasoning">
441
+ <div class="reasoning-box">
442
+ <p><strong>🤔 Reasoning:</strong></p>
443
+ <p>
444
+ '''
445
+
446
+ # Create streaming session
447
+ response_stream = model.create_chat_completion(
448
+ messages=[
449
+ {"role": "system", "content": SYSTEM_PROMPT},
450
+ {"role": "user", "content": game_state}
451
+ ],
452
+ temperature=temperature,
453
+ top_p=0.95,
454
+ max_tokens=1024,
455
+ stream=True
456
+ )
457
+
458
+ # Process stream
459
+ collected_text = ""
460
+ for chunk in response_stream:
461
+ if 'choices' in chunk and len(chunk['choices']) > 0:
462
+ delta = chunk['choices'][0]['delta']
463
+ if 'content' in delta:
464
+ content = delta['content']
465
  if content:
466
+ collected_text += content
467
  full_response += content
468
 
469
+ # Extract and update reasoning
470
+ if "<reasoning>" in collected_text and "</reasoning>" not in collected_text:
471
+ # Still collecting reasoning
472
+ parts = collected_text.split("<reasoning>")
473
  if len(parts) > 1:
474
+ reasoning_content = parts[1]
475
+ current_html = reasoning_html + reasoning_content + '</p>'
476
+ if not move_str:
477
+ current_html += '</div></div>'
478
+ yield [
479
+ render_board(game.board),
480
+ "AI is thinking...",
481
+ current_html
482
+ ]
 
 
 
483
 
484
+ # Check for complete reasoning
485
+ if "<reasoning>" in collected_text and "</reasoning>" in collected_text:
486
+ reasoning_content = extract_reasoning(collected_text)
487
 
488
+ # Check for move
489
+ new_move = extract_xml_move(collected_text)
490
+ if new_move and not move_str:
491
+ move_str = new_move
492
+ current_html = reasoning_html + reasoning_content + '</p>'
493
+ current_html += f'<p><strong>📍 Move chosen:</strong> Column {move_str.upper()}</p>'
494
+ current_html += '</div></div>'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
495
  yield [
496
  render_board(game.board),
497
+ "AI is making a move...",
498
+ current_html
499
  ]
500
+
501
+ # Final formatting
502
+ final_reasoning_html = f'''
503
+ <div id="ai-reasoning">
504
+ <div class="reasoning-box">
505
+ <p><strong>🤔 Reasoning:</strong></p>
506
+ <p>{reasoning_content if reasoning_content else "AI couldn't provide clear reasoning."}</p>
507
+ <p><strong>📍 Move chosen:</strong> Column {move_str.upper() if move_str else "?"}</p>
508
+ </div>
509
+ </div>
510
+ '''
511
+
512
+ # Make AI move
513
+ if move_str:
514
+ ai_col = game.parse_ai_move(move_str)
515
 
516
+ if ai_col != -1:
517
+ success, _ = game.make_move(ai_col)
518
+ if success:
519
+ # Check for AI winner
520
+ winner = game.check_winner()
521
+ if winner == 2:
522
+ game.game_over = True
523
+ yield [
524
+ render_board(game.board),
525
+ "🤖 AI wins! Better luck next time!",
526
+ final_reasoning_html
527
+ ]
528
+ return
529
+ else:
530
+ # AI made a valid move but didn't win
531
+ game.current_player = 1
532
+ yield [
533
+ render_board(game.board),
534
+ "Your turn!",
535
+ final_reasoning_html
536
+ ]
537
+ return
538
 
539
+ # If we get here, something went wrong with the AI move
540
+ game.game_over = True
541
+ yield [
542
+ render_board(game.board),
543
+ "AI made an invalid move! You win by default!",
544
+ '<div id="ai-reasoning">AI made an invalid move or provided an invalid response!</div>'
545
+ ]
546
 
547
  def reset_game():
548
  game.board = np.zeros((6, 7))
 
558
 
559
  # Event handlers
560
  for i, btn in enumerate(col_buttons):
561
+ # First handle player move
562
  btn.click(
563
+ fn=handle_player_move,
564
  inputs=[
565
  gr.Number(value=i, visible=False),
566
  temperature_slider
567
  ],
568
+ outputs=[board_display, status, reasoning_display],
569
+ queue=False # Process player move immediately
570
+ ).then( # Then process AI response with streaming
571
+ fn=process_ai_response,
572
+ inputs=[temperature_slider],
573
+ outputs=[board_display, status, reasoning_display],
574
+ streaming=True # Enable streaming for AI response
575
  )
576
 
577
  reset_btn.click(