Spaces:
				
			
			
	
			
			
					
		Running
		
	
	
	
			
			
	
	
	
	
		
		
					
		Running
		
	| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>LLM Hill Climber - HuggingFace Spaces</title> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css"> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| min-height: 100vh; | |
| color: #333; | |
| } | |
| .header { | |
| background: rgba(255, 255, 255, 0.1); | |
| backdrop-filter: blur(10px); | |
| padding: 20px; | |
| text-align: center; | |
| border-bottom: 1px solid rgba(255, 255, 255, 0.2); | |
| } | |
| .header h1 { | |
| color: white; | |
| font-size: 2.5em; | |
| margin-bottom: 10px; | |
| text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); | |
| } | |
| .header p { | |
| color: rgba(255, 255, 255, 0.9); | |
| font-size: 1.1em; | |
| } | |
| .spaces-info { | |
| background: rgba(255, 255, 255, 0.95); | |
| margin: 20px auto; | |
| max-width: 1200px; | |
| padding: 15px; | |
| border-radius: 10px; | |
| border-left: 4px solid #667eea; | |
| } | |
| .container { | |
| max-width: 1400px; | |
| margin: 0 auto; | |
| padding: 20px; | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 20px; | |
| min-height: calc(100vh - 200px); | |
| } | |
| .panel { | |
| background: rgba(255, 255, 255, 0.95); | |
| border-radius: 20px; | |
| padding: 25px; | |
| box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); | |
| backdrop-filter: blur(10px); | |
| } | |
| .panel h2 { | |
| color: #333; | |
| margin-bottom: 20px; | |
| font-size: 1.5em; | |
| border-bottom: 3px solid #667eea; | |
| padding-bottom: 10px; | |
| } | |
| .model-info { | |
| background: #e8f5e8; | |
| padding: 15px; | |
| border-radius: 8px; | |
| margin-bottom: 20px; | |
| border-left: 4px solid #4caf50; | |
| } | |
| .problem-selector { | |
| margin-bottom: 20px; | |
| } | |
| .problem-selector select { | |
| width: 100%; | |
| padding: 12px; | |
| border: 2px solid #e0e0e0; | |
| border-radius: 8px; | |
| font-size: 16px; | |
| background: white; | |
| } | |
| .problem-selector select:focus { | |
| border-color: #667eea; | |
| outline: none; | |
| } | |
| .problem-details { | |
| background: #f8f9fa; | |
| padding: 20px; | |
| border-radius: 12px; | |
| margin-bottom: 20px; | |
| border-left: 4px solid #667eea; | |
| } | |
| .problem-details h3 { | |
| color: #333; | |
| margin-bottom: 10px; | |
| } | |
| .problem-details p { | |
| line-height: 1.6; | |
| margin-bottom: 15px; | |
| } | |
| .test-cases { | |
| background: #2d3748; | |
| color: #e2e8f0; | |
| padding: 15px; | |
| border-radius: 8px; | |
| font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; | |
| font-size: 14px; | |
| overflow-x: auto; | |
| white-space: pre-wrap; | |
| } | |
| .controls { | |
| display: flex; | |
| gap: 15px; | |
| margin-bottom: 20px; | |
| flex-wrap: wrap; | |
| } | |
| .btn { | |
| padding: 12px 24px; | |
| border: none; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| font-size: 16px; | |
| font-weight: bold; | |
| transition: all 0.3s ease; | |
| text-transform: uppercase; | |
| letter-spacing: 0.5px; | |
| } | |
| .btn-primary { | |
| background: linear-gradient(45deg, #667eea, #764ba2); | |
| color: white; | |
| box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3); | |
| } | |
| .btn-primary:hover:not(:disabled) { | |
| transform: translateY(-2px); | |
| box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4); | |
| } | |
| .btn-danger { | |
| background: linear-gradient(45deg, #ff6b6b, #ee5a24); | |
| color: white; | |
| box-shadow: 0 4px 15px rgba(255, 107, 107, 0.3); | |
| } | |
| .btn-danger:hover:not(:disabled) { | |
| transform: translateY(-2px); | |
| box-shadow: 0 8px 25px rgba(255, 107, 107, 0.4); | |
| } | |
| .btn:disabled { | |
| opacity: 0.6; | |
| cursor: not-allowed; | |
| transform: none; | |
| } | |
| .settings { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 15px; | |
| margin-bottom: 20px; | |
| } | |
| .setting-group { | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .setting-group label { | |
| font-weight: bold; | |
| margin-bottom: 5px; | |
| color: #555; | |
| } | |
| .setting-group input { | |
| padding: 8px; | |
| border: 2px solid #e0e0e0; | |
| border-radius: 6px; | |
| font-size: 14px; | |
| } | |
| .setting-group input:focus { | |
| border-color: #667eea; | |
| outline: none; | |
| } | |
| .progress-section { | |
| margin-bottom: 20px; | |
| } | |
| .progress-bar { | |
| width: 100%; | |
| height: 20px; | |
| background: #e0e0e0; | |
| border-radius: 10px; | |
| overflow: hidden; | |
| margin-bottom: 10px; | |
| } | |
| .progress-fill { | |
| height: 100%; | |
| background: linear-gradient(45deg, #667eea, #764ba2); | |
| width: 0%; | |
| transition: width 0.3s ease; | |
| } | |
| .progress-text { | |
| text-align: center; | |
| font-weight: bold; | |
| color: #555; | |
| } | |
| .evolution-log { | |
| background: #2d3748; | |
| color: #e2e8f0; | |
| padding: 15px; | |
| border-radius: 8px; | |
| height: 250px; | |
| overflow-y: auto; | |
| font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; | |
| font-size: 12px; | |
| line-height: 1.4; | |
| margin-bottom: 20px; | |
| } | |
| .current-solution { | |
| background: #f8f9fa; | |
| border: 2px solid #e0e0e0; | |
| border-radius: 8px; | |
| padding: 15px; | |
| max-height: 300px; | |
| overflow-y: auto; | |
| } | |
| .current-solution pre { | |
| background: #2d3748; | |
| color: #e2e8f0; | |
| padding: 15px; | |
| border-radius: 8px; | |
| overflow-x: auto; | |
| margin: 0; | |
| } | |
| .fitness-chart { | |
| background: white; | |
| padding: 15px; | |
| border-radius: 8px; | |
| border: 2px solid #e0e0e0; | |
| margin-bottom: 20px; | |
| } | |
| .status { | |
| padding: 15px; | |
| border-radius: 8px; | |
| margin-bottom: 20px; | |
| font-weight: bold; | |
| text-align: center; | |
| } | |
| .status-idle { | |
| background: #e3f2fd; | |
| color: #1976d2; | |
| border: 2px solid #1976d2; | |
| } | |
| .status-running { | |
| background: #fff3e0; | |
| color: #f57c00; | |
| border: 2px solid #f57c00; | |
| } | |
| .status-complete { | |
| background: #e8f5e8; | |
| color: #2e7d32; | |
| border: 2px solid #2e7d32; | |
| } | |
| .status-error { | |
| background: #ffebee; | |
| color: #c62828; | |
| border: 2px solid #c62828; | |
| } | |
| .spinner { | |
| display: inline-block; | |
| width: 20px; | |
| height: 20px; | |
| border: 2px solid #f3f3f3; | |
| border-top: 2px solid #667eea; | |
| border-radius: 50%; | |
| animation: spin 1s linear infinite; | |
| margin-right: 10px; | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| .best-solution { | |
| background: #e8f5e8; | |
| border: 2px solid #4caf50; | |
| border-radius: 8px; | |
| padding: 20px; | |
| margin-top: 20px; | |
| } | |
| .best-solution h3 { | |
| color: #2e7d32; | |
| margin-bottom: 15px; | |
| } | |
| .api-efficiency { | |
| background: #fff3e0; | |
| padding: 15px; | |
| border-radius: 8px; | |
| margin-bottom: 20px; | |
| border-left: 4px solid #f57c00; | |
| } | |
| @media (max-width: 1200px) { | |
| .container { | |
| grid-template-columns: 1fr; | |
| gap: 15px; | |
| } | |
| } | |
| @media (max-width: 768px) { | |
| .header h1 { | |
| font-size: 2em; | |
| } | |
| .controls { | |
| flex-direction: column; | |
| } | |
| .settings { | |
| grid-template-columns: 1fr; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="header"> | |
| <h1>𧬠LLM Hill Climber</h1> | |
| <p>Efficient program evolution using StarCoder2-3B on HuggingFace Spaces</p> | |
| </div> | |
| <div class="spaces-info"> | |
| <h3>π HuggingFace Spaces Deployment</h3> | |
| <p><strong>Model:</strong> bigcode/starcoder2-3b (specialized coding AI) β’ <strong>Strategy:</strong> Single mutation per generation β’ <strong>Auth:</strong> Add your HF token above or get one free at <a href="https://huggingface.co/settings/tokens" target="_blank" style="color: #667eea;">huggingface.co/settings/tokens</a></p> | |
| </div> | |
| <div class="container"> | |
| <!-- Left Panel: Problem Setup & Controls --> | |
| <div class="panel"> | |
| <h2>π― Problem Configuration</h2> | |
| <div class="model-info"> | |
| <h4>π€ AI Model Selection</h4> | |
| <select id="modelSelect" style="width: 100%; padding: 8px; border: 2px solid #e0e0e0; border-radius: 6px; margin-bottom: 10px;"> | |
| <option value="starcoder2-3b">StarCoder2-3B (Recommended)</option> | |
| <option value="starcoder2-7b">StarCoder2-7B (Better quality)</option> | |
| <option value="codellama-7b">CodeLlama-7B (Alternative)</option> | |
| <option value="codebert-base">CodeBERT-Base (Fallback)</option> | |
| </select> | |
| <p style="margin-bottom: 10px;">Specialized coding models by BigCode and Meta. Excellent at JavaScript generation.</p> | |
| <div style="margin-top: 10px;"> | |
| <label for="hfToken" style="font-weight: bold; display: block; margin-bottom: 5px;">HuggingFace Token:</label> | |
| <input type="password" id="hfToken" placeholder="hf_xxxxx... (required for API access)" style="width: 100%; padding: 8px; border: 2px solid #e0e0e0; border-radius: 6px;"> | |
| <small style="color: #666;">Get a free token at <a href="https://huggingface.co/settings/tokens" target="_blank">huggingface.co/settings/tokens</a></small> | |
| </div> | |
| </div> | |
| <div class="problem-selector"> | |
| <select id="problemSelect"> | |
| <option value="">Select a coding problem...</option> | |
| <option value="palindrome">Palindrome Checker</option> | |
| <option value="two_sum">Two Sum Problem</option> | |
| <option value="fibonacci">Fibonacci Generator</option> | |
| <option value="prime_check">Prime Number Checker</option> | |
| <option value="reverse_string">String Reversal</option> | |
| <option value="max_subarray">Maximum Subarray Sum</option> | |
| <option value="binary_search">Binary Search</option> | |
| <option value="anagram_check">Anagram Checker</option> | |
| </select> | |
| </div> | |
| <div class="problem-details" id="problemDetails"> | |
| <h3>Select a problem to begin</h3> | |
| <p>Choose a programming challenge for the AI to evolve solutions for.</p> | |
| </div> | |
| <div class="settings"> | |
| <div class="setting-group"> | |
| <label for="maxGenerations">Max Generations:</label> | |
| <input type="number" id="maxGenerations" value="12" min="5" max="25"> | |
| </div> | |
| <div class="setting-group"> | |
| <label for="temperature">Temperature:</label> | |
| <input type="range" id="temperature" value="0.3" min="0.1" max="0.8" step="0.1"> | |
| </div> | |
| <div class="setting-group"> | |
| <label for="maxTokens">Max Tokens:</label> | |
| <input type="number" id="maxTokens" value="384" min="256" max="512" step="64"> | |
| </div> | |
| <div class="setting-group"> | |
| <label for="retryDelay">Retry Delay (ms):</label> | |
| <input type="number" id="retryDelay" value="2000" min="1000" max="5000" step="500"> | |
| </div> | |
| </div> | |
| <div class="api-efficiency"> | |
| <h4>β‘ API Efficiency</h4> | |
| <p><strong>Single Mutation Strategy:</strong> One API call per generation for maximum efficiency. Estimated total: <span id="estimatedCalls">12</span> API calls.</p> | |
| </div> | |
| <div class="controls"> | |
| <button class="btn btn-primary" id="startBtn">π Start Evolution</button> | |
| <button class="btn btn-danger" id="stopBtn" disabled>βΉοΈ Stop</button> | |
| </div> | |
| <div class="status status-idle" id="status"> | |
| Ready to evolve JavaScript programs | |
| </div> | |
| <div class="progress-section"> | |
| <div class="progress-bar"> | |
| <div class="progress-fill" id="progressFill"></div> | |
| </div> | |
| <div class="progress-text" id="progressText">0 / 0 generations</div> | |
| </div> | |
| <h2>π Fitness Evolution</h2> | |
| <div class="fitness-chart"> | |
| <canvas id="fitnessChart" width="400" height="200"></canvas> | |
| </div> | |
| </div> | |
| <!-- Right Panel: Evolution Progress & Results --> | |
| <div class="panel"> | |
| <h2>π¬ Evolution Progress</h2> | |
| <div class="evolution-log" id="evolutionLog"> | |
| Ready to start evolution process... | |
| 𧬠Hill Climber Algorithm: | |
| β’ Generate initial solution with StarCoder2-3B | |
| β’ Each generation: create one mutation | |
| β’ Keep mutation if fitness improves | |
| β’ Stop when perfect solution found or max generations reached | |
| </div> | |
| <h2>π» Current Best Solution</h2> | |
| <div class="current-solution" id="currentSolution"> | |
| <p style="text-align: center; color: #666; padding: 20px;"> | |
| No solution generated yet | |
| </p> | |
| </div> | |
| <div class="best-solution" id="bestSolution" style="display: none;"> | |
| <h3>π Final Best Solution</h3> | |
| <div id="bestSolutionCode"></div> | |
| <div id="bestSolutionStats"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Model options for different scenarios | |
| const MODELS = { | |
| 'starcoder2-3b': 'https://api-inference.huggingface.co/models/bigcode/starcoder2-3b', | |
| 'starcoder2-7b': 'https://api-inference.huggingface.co/models/bigcode/starcoder2-7b', | |
| 'codellama-7b': 'https://api-inference.huggingface.co/models/codellama/CodeLlama-7b-Instruct-hf', | |
| 'codebert-base': 'https://api-inference.huggingface.co/models/microsoft/codebert-base' | |
| }; | |
| // Default model | |
| let MODEL_URL = MODELS['starcoder2-3b']; | |
| // Problem definitions optimized for StarCoder2 | |
| const PROBLEMS = { | |
| palindrome: { | |
| name: "Palindrome Checker", | |
| description: "Write a JavaScript function that checks if a string is a palindrome (reads the same forwards and backwards). Ignore case and non-alphanumeric characters.", | |
| functionName: "isPalindrome", | |
| parameters: "s", | |
| testCases: [ | |
| { input: ["racecar"], expected: true }, | |
| { input: ["A man a plan a canal Panama"], expected: true }, | |
| { input: ["race a car"], expected: false }, | |
| { input: [""], expected: true }, | |
| { input: ["Madam"], expected: true } | |
| ] | |
| }, | |
| two_sum: { | |
| name: "Two Sum Problem", | |
| description: "Given an array of integers and a target sum, return the indices of two numbers that add up to the target. Assume exactly one solution exists.", | |
| functionName: "twoSum", | |
| parameters: "nums, target", | |
| testCases: [ | |
| { input: [[2, 7, 11, 15], 9], expected: [0, 1] }, | |
| { input: [[3, 2, 4], 6], expected: [1, 2] }, | |
| { input: [[3, 3], 6], expected: [0, 1] }, | |
| { input: [[1, 5, 3, 7], 8], expected: [1, 3] } | |
| ] | |
| }, | |
| fibonacci: { | |
| name: "Fibonacci Generator", | |
| description: "Generate the nth Fibonacci number efficiently. Handle edge cases for n=0 and n=1.", | |
| functionName: "fibonacci", | |
| parameters: "n", | |
| testCases: [ | |
| { input: [0], expected: 0 }, | |
| { input: [1], expected: 1 }, | |
| { input: [2], expected: 1 }, | |
| { input: [10], expected: 55 }, | |
| { input: [15], expected: 610 } | |
| ] | |
| }, | |
| prime_check: { | |
| name: "Prime Number Checker", | |
| description: "Determine if a given positive integer is a prime number. A prime number is only divisible by 1 and itself.", | |
| functionName: "isPrime", | |
| parameters: "n", | |
| testCases: [ | |
| { input: [2], expected: true }, | |
| { input: [3], expected: true }, | |
| { input: [4], expected: false }, | |
| { input: [17], expected: true }, | |
| { input: [25], expected: false }, | |
| { input: [1], expected: false } | |
| ] | |
| }, | |
| reverse_string: { | |
| name: "String Reversal", | |
| description: "Write a function that reverses a string. Handle empty strings appropriately.", | |
| functionName: "reverseString", | |
| parameters: "s", | |
| testCases: [ | |
| { input: ["hello"], expected: "olleh" }, | |
| { input: ["world"], expected: "dlrow" }, | |
| { input: [""], expected: "" }, | |
| { input: ["a"], expected: "a" }, | |
| { input: ["JavaScript"], expected: "tpircSavaJ" } | |
| ] | |
| }, | |
| max_subarray: { | |
| name: "Maximum Subarray Sum", | |
| description: "Find the contiguous subarray with the largest sum and return the sum (Kadane's algorithm).", | |
| functionName: "maxSubarraySum", | |
| parameters: "nums", | |
| testCases: [ | |
| { input: [[-2, 1, -3, 4, -1, 2, 1, -5, 4]], expected: 6 }, | |
| { input: [[1]], expected: 1 }, | |
| { input: [[5, 4, -1, 7, 8]], expected: 23 }, | |
| { input: [[-1]], expected: -1 } | |
| ] | |
| }, | |
| binary_search: { | |
| name: "Binary Search", | |
| description: "Implement binary search to find the index of a target value in a sorted array. Return -1 if not found.", | |
| functionName: "binarySearch", | |
| parameters: "arr, target", | |
| testCases: [ | |
| { input: [[-1, 0, 3, 5, 9, 12], 9], expected: 4 }, | |
| { input: [[-1, 0, 3, 5, 9, 12], 2], expected: -1 }, | |
| { input: [[1, 2, 3, 4, 5], 1], expected: 0 }, | |
| { input: [[1, 2, 3, 4, 5], 5], expected: 4 } | |
| ] | |
| }, | |
| anagram_check: { | |
| name: "Anagram Checker", | |
| description: "Check if two strings are anagrams of each other. Ignore case and spaces.", | |
| functionName: "areAnagrams", | |
| parameters: "s1, s2", | |
| testCases: [ | |
| { input: ["listen", "silent"], expected: true }, | |
| { input: ["evil", "vile"], expected: true }, | |
| { input: ["hello", "bello"], expected: false }, | |
| { input: ["A gentleman", "Elegant man"], expected: true } | |
| ] | |
| } | |
| }; | |
| // Hill Climber state | |
| let isRunning = false; | |
| let currentGeneration = 0; | |
| let maxGenerations = 12; | |
| let currentProblem = null; | |
| let currentSolution = null; | |
| let bestSolution = null; | |
| let bestFitness = -1; | |
| let fitnessHistory = []; | |
| let chart = null; | |
| let apiCallCount = 0; | |
| // DOM elements | |
| const problemSelect = document.getElementById('problemSelect'); | |
| const problemDetails = document.getElementById('problemDetails'); | |
| const startBtn = document.getElementById('startBtn'); | |
| const stopBtn = document.getElementById('stopBtn'); | |
| const status = document.getElementById('status'); | |
| const progressFill = document.getElementById('progressFill'); | |
| const progressText = document.getElementById('progressText'); | |
| const evolutionLog = document.getElementById('evolutionLog'); | |
| const currentSolutionDiv = document.getElementById('currentSolution'); | |
| const bestSolutionDiv = document.getElementById('bestSolution'); | |
| const estimatedCalls = document.getElementById('estimatedCalls'); | |
| // Initialize | |
| initializeChart(); | |
| setupEventListeners(); | |
| function initializeChart() { | |
| const ctx = document.getElementById('fitnessChart').getContext('2d'); | |
| chart = new Chart(ctx, { | |
| type: 'line', | |
| data: { | |
| labels: [], | |
| datasets: [{ | |
| label: 'Fitness Score', | |
| data: [], | |
| borderColor: '#667eea', | |
| backgroundColor: 'rgba(102, 126, 234, 0.1)', | |
| tension: 0.4, | |
| fill: true | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| scales: { | |
| y: { | |
| beginAtZero: true, | |
| max: 1 | |
| } | |
| }, | |
| plugins: { | |
| legend: { | |
| display: false | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| function setupEventListeners() { | |
| problemSelect.addEventListener('change', handleProblemSelect); | |
| startBtn.addEventListener('click', startEvolution); | |
| stopBtn.addEventListener('click', stopEvolution); | |
| // Model selector | |
| document.getElementById('modelSelect').addEventListener('change', (e) => { | |
| MODEL_URL = MODELS[e.target.value]; | |
| log(`Model changed to: ${e.target.options[e.target.selectedIndex].text}`); | |
| }); | |
| // Update estimated API calls when generations change | |
| document.getElementById('maxGenerations').addEventListener('input', (e) => { | |
| estimatedCalls.textContent = e.target.value; | |
| }); | |
| } | |
| function handleProblemSelect() { | |
| const selectedProblem = problemSelect.value; | |
| if (selectedProblem) { | |
| currentProblem = PROBLEMS[selectedProblem]; | |
| displayProblemDetails(currentProblem); | |
| startBtn.disabled = false; | |
| } else { | |
| currentProblem = null; | |
| problemDetails.innerHTML = ` | |
| <h3>Select a problem to begin</h3> | |
| <p>Choose a programming challenge for the AI to evolve solutions for.</p> | |
| `; | |
| startBtn.disabled = true; | |
| } | |
| } | |
| function displayProblemDetails(problem) { | |
| const testCasesText = problem.testCases.map((tc, i) => | |
| `Test ${i + 1}: ${problem.functionName}(${tc.input.map(inp => JSON.stringify(inp)).join(', ')}) β ${JSON.stringify(tc.expected)}` | |
| ).join('\n'); | |
| problemDetails.innerHTML = ` | |
| <h3>${problem.name}</h3> | |
| <p><strong>Description:</strong> ${problem.description}</p> | |
| <p><strong>Function signature:</strong> <code>function ${problem.functionName}(${problem.parameters}) { ... }</code></p> | |
| <div class="test-cases">${testCasesText}</div> | |
| `; | |
| } | |
| async function startEvolution() { | |
| if (!currentProblem) { | |
| alert('Please select a problem first'); | |
| return; | |
| } | |
| // Check for HF token | |
| const hfToken = document.getElementById('hfToken').value.trim(); | |
| if (!hfToken) { | |
| alert('Please add your HuggingFace token above. Get a free one at: https://huggingface.co/settings/tokens'); | |
| return; | |
| } | |
| if (!hfToken.startsWith('hf_')) { | |
| alert('Invalid token format. HuggingFace tokens start with "hf_"'); | |
| return; | |
| } | |
| isRunning = true; | |
| currentGeneration = 0; | |
| maxGenerations = parseInt(document.getElementById('maxGenerations').value); | |
| fitnessHistory = []; | |
| bestSolution = null; | |
| bestFitness = -1; | |
| apiCallCount = 0; | |
| updateUI(); | |
| updateStatus('running', 'Evolution in progress...'); | |
| log('π Starting evolution with StarCoder2-3B...'); | |
| log(`Problem: ${currentProblem.name}`); | |
| log(`Strategy: Single mutation per generation (API efficient)`); | |
| log(`Max generations: ${maxGenerations}`); | |
| log(''); | |
| try { | |
| // Step 1: Generate initial solution | |
| log('π¬ Generating initial solution...'); | |
| currentSolution = await generateInitialSolution(); | |
| apiCallCount++; | |
| if (!currentSolution) { | |
| throw new Error('Failed to generate initial solution'); | |
| } | |
| log(`β Initial solution generated (API calls: ${apiCallCount})`); | |
| // Evaluate initial solution | |
| let currentFitness = evaluateSolution(currentSolution); | |
| fitnessHistory.push(currentFitness); | |
| bestSolution = currentSolution; | |
| bestFitness = currentFitness; | |
| log(`Initial fitness: ${currentFitness.toFixed(3)} (${Math.round(currentFitness * currentProblem.testCases.length)}/${currentProblem.testCases.length} tests)`); | |
| updateProgress(); | |
| updateChart(); | |
| updateCurrentSolution(); | |
| // Step 2: Evolution loop | |
| for (currentGeneration = 1; currentGeneration <= maxGenerations && isRunning; currentGeneration++) { | |
| log(`\nπ Generation ${currentGeneration}/${maxGenerations}`); | |
| // Check if perfect solution found | |
| if (currentFitness >= 1.0) { | |
| log('π Perfect solution found! Stopping evolution.'); | |
| break; | |
| } | |
| // Generate single mutation | |
| log('𧬠Generating mutation...'); | |
| const mutatedSolution = await mutateSolution(currentSolution, currentFitness); | |
| apiCallCount++; | |
| if (!mutatedSolution) { | |
| log('β οΈ Mutation failed, keeping current solution'); | |
| continue; | |
| } | |
| // Evaluate mutation | |
| const mutatedFitness = evaluateSolution(mutatedSolution); | |
| log(`Mutation fitness: ${mutatedFitness.toFixed(3)} (${Math.round(mutatedFitness * currentProblem.testCases.length)}/${currentProblem.testCases.length} tests)`); | |
| // Hill Climber decision: accept if better or equal | |
| if (mutatedFitness >= currentFitness) { | |
| currentSolution = mutatedSolution; | |
| currentFitness = mutatedFitness; | |
| log('β Mutation accepted (fitness improved or maintained)'); | |
| // Update best solution | |
| if (mutatedFitness > bestFitness) { | |
| bestSolution = mutatedSolution; | |
| bestFitness = mutatedFitness; | |
| log('β¨ New best solution found!'); | |
| } | |
| } else { | |
| log('β Mutation rejected (fitness decreased)'); | |
| } | |
| fitnessHistory.push(Math.max(currentFitness, bestFitness)); | |
| log(`API calls used: ${apiCallCount}`); | |
| updateProgress(); | |
| updateChart(); | |
| updateCurrentSolution(); | |
| // Add delay to be respectful to API | |
| const delay = parseInt(document.getElementById('retryDelay').value); | |
| await new Promise(resolve => setTimeout(resolve, delay)); | |
| } | |
| // Evolution complete | |
| if (isRunning) { | |
| updateStatus('complete', `Evolution completed! Best fitness: ${bestFitness.toFixed(3)}`); | |
| log(`\nπ Evolution completed!`); | |
| log(`Best fitness: ${bestFitness.toFixed(3)}`); | |
| log(`Total API calls: ${apiCallCount}`); | |
| showBestSolution(); | |
| } | |
| } catch (error) { | |
| updateStatus('error', `Error: ${error.message}`); | |
| log(`β Error: ${error.message}`); | |
| console.error('Evolution error:', error); | |
| } finally { | |
| isRunning = false; | |
| updateUI(); | |
| } | |
| } | |
| function stopEvolution() { | |
| isRunning = false; | |
| updateStatus('idle', 'Evolution stopped by user'); | |
| log('\nβΉοΈ Evolution stopped by user'); | |
| updateUI(); | |
| } | |
| async function generateInitialSolution() { | |
| const prompt = `Write a JavaScript function to solve this coding problem: | |
| **Problem:** ${currentProblem.description} | |
| **Function signature:** function ${currentProblem.functionName}(${currentProblem.parameters}) { } | |
| **Test cases:** | |
| ${currentProblem.testCases.map(tc => | |
| `${currentProblem.functionName}(${tc.input.map(inp => JSON.stringify(inp)).join(', ')}) should return ${JSON.stringify(tc.expected)}` | |
| ).join('\n')} | |
| Write only the complete JavaScript function with proper logic to pass all test cases:`; | |
| return await callStarCoder(prompt); | |
| } | |
| async function mutateSolution(solution, currentFitness) { | |
| const failingCases = []; | |
| const passingCases = []; | |
| for (const testCase of currentProblem.testCases) { | |
| const result = executeTestCase(solution, testCase); | |
| if (result.passed) { | |
| passingCases.push(testCase); | |
| } else { | |
| failingCases.push({...testCase, actualOutput: result.actualOutput}); | |
| } | |
| } | |
| let prompt; | |
| if (failingCases.length > 0) { | |
| // Focus on fixing failing cases | |
| prompt = `Fix this JavaScript function to pass all test cases: | |
| **Current function:** | |
| ${solution} | |
| **Failing test cases:** | |
| ${failingCases.map(fc => | |
| `${currentProblem.functionName}(${fc.input.map(inp => JSON.stringify(inp)).join(', ')}) returns ${JSON.stringify(fc.actualOutput)} but should return ${JSON.stringify(fc.expected)}` | |
| ).join('\n')} | |
| **Passing test cases:** ${passingCases.length}/${currentProblem.testCases.length} | |
| Write the improved JavaScript function:`; | |
| } else { | |
| // Optimize already working solution | |
| prompt = `Optimize this working JavaScript function for better performance or readability: | |
| **Current function:** | |
| ${solution} | |
| **All test cases are passing** | |
| Write an optimized version of the JavaScript function:`; | |
| } | |
| return await callStarCoder(prompt); | |
| } | |
| async function callStarCoder(prompt) { | |
| const maxTokens = parseInt(document.getElementById('maxTokens').value); | |
| const temperature = parseFloat(document.getElementById('temperature').value); | |
| // Get HF token from user input | |
| const hfToken = document.getElementById('hfToken').value.trim(); | |
| try { | |
| const response = await fetch(MODEL_URL, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Authorization': `Bearer ${hfToken}` | |
| }, | |
| body: JSON.stringify({ | |
| inputs: prompt, | |
| parameters: { | |
| max_new_tokens: maxTokens, | |
| temperature: temperature, | |
| top_p: 0.9, | |
| do_sample: true, | |
| return_full_text: false | |
| } | |
| }) | |
| }); | |
| if (!response.ok) { | |
| if (response.status === 401) { | |
| throw new Error('Authentication failed. Please add a valid HuggingFace token above.'); | |
| } | |
| if (response.status === 503) { | |
| log('β οΈ Model loading, retrying in 10s...'); | |
| await new Promise(resolve => setTimeout(resolve, 10000)); | |
| return await callStarCoder(prompt); | |
| } | |
| if (response.status === 429) { | |
| throw new Error('Rate limit exceeded. Please wait and try again, or use your own HF token.'); | |
| } | |
| throw new Error(`API request failed: ${response.status} ${response.statusText}`); | |
| } | |
| const data = await response.json(); | |
| if (data.error) { | |
| throw new Error(data.error); | |
| } | |
| let generatedText = data[0]?.generated_text || ''; | |
| // Clean and extract JavaScript function | |
| return extractJavaScriptFunction(generatedText); | |
| } catch (error) { | |
| log(`β οΈ API call failed: ${error.message}`); | |
| throw error; | |
| } | |
| } | |
| function extractJavaScriptFunction(text) { | |
| // Clean the generated text | |
| text = text.trim(); | |
| // Look for function definition | |
| const functionMatch = text.match(/function\s+\w+[^{]*\{[^]*?\n}/); | |
| if (functionMatch) { | |
| return functionMatch[0]; | |
| } | |
| // Look for arrow function | |
| const arrowMatch = text.match(/const\s+\w+\s*=\s*\([^)]*\)\s*=>\s*\{[^]*?\n}/); | |
| if (arrowMatch) { | |
| return arrowMatch[0]; | |
| } | |
| // If no clear function found, try to construct one | |
| if (currentProblem) { | |
| const lines = text.split('\n').filter(line => line.trim()); | |
| if (lines.length > 0) { | |
| return `function ${currentProblem.functionName}(${currentProblem.parameters}) {\n ${lines.join('\n ')}\n}`; | |
| } | |
| } | |
| return text; | |
| } | |
| function evaluateSolution(solution) { | |
| let passedTests = 0; | |
| for (const testCase of currentProblem.testCases) { | |
| const result = executeTestCase(solution, testCase); | |
| if (result.passed) { | |
| passedTests++; | |
| } | |
| } | |
| return passedTests / currentProblem.testCases.length; | |
| } | |
| function executeTestCase(solution, testCase) { | |
| try { | |
| const func = new Function(` | |
| ${solution} | |
| return ${currentProblem.functionName}(...arguments); | |
| `); | |
| const actualOutput = func(...testCase.input); | |
| const passed = JSON.stringify(actualOutput) === JSON.stringify(testCase.expected); | |
| return { passed, actualOutput }; | |
| } catch (error) { | |
| return { passed: false, actualOutput: `Error: ${error.message}` }; | |
| } | |
| } | |
| function updateUI() { | |
| startBtn.disabled = isRunning || !currentProblem; | |
| stopBtn.disabled = !isRunning; | |
| problemSelect.disabled = isRunning; | |
| } | |
| function updateStatus(type, message) { | |
| status.className = `status status-${type}`; | |
| if (type === 'running') { | |
| status.innerHTML = `<span class="spinner"></span>${message}`; | |
| } else { | |
| status.textContent = message; | |
| } | |
| } | |
| function updateProgress() { | |
| const progress = (currentGeneration / maxGenerations) * 100; | |
| progressFill.style.width = `${progress}%`; | |
| progressText.textContent = `${currentGeneration} / ${maxGenerations} generations`; | |
| } | |
| function updateChart() { | |
| chart.data.labels.push(currentGeneration === 0 ? 'Initial' : `Gen ${currentGeneration}`); | |
| chart.data.datasets[0].data.push(fitnessHistory[fitnessHistory.length - 1]); | |
| chart.update('none'); | |
| } | |
| function updateCurrentSolution() { | |
| if (bestSolution) { | |
| currentSolutionDiv.innerHTML = ` | |
| <pre><code class="language-javascript">${escapeHtml(bestSolution)}</code></pre> | |
| `; | |
| hljs.highlightAll(); | |
| } | |
| } | |
| function showBestSolution() { | |
| if (bestSolution) { | |
| const passedTests = Math.round(bestFitness * currentProblem.testCases.length); | |
| const totalTests = currentProblem.testCases.length; | |
| document.getElementById('bestSolutionCode').innerHTML = ` | |
| <pre><code class="language-javascript">${escapeHtml(bestSolution)}</code></pre> | |
| `; | |
| document.getElementById('bestSolutionStats').innerHTML = ` | |
| <p><strong>Final Fitness:</strong> ${bestFitness.toFixed(3)}</p> | |
| <p><strong>Test Cases Passed:</strong> ${passedTests} / ${totalTests}</p> | |
| <p><strong>Generations Required:</strong> ${currentGeneration}</p> | |
| <p><strong>Total API Calls:</strong> ${apiCallCount}</p> | |
| <p><strong>Model Used:</strong> StarCoder2-3B</p> | |
| `; | |
| bestSolutionDiv.style.display = 'block'; | |
| hljs.highlightAll(); | |
| } | |
| } | |
| function log(message) { | |
| evolutionLog.innerHTML += message + '\n'; | |
| evolutionLog.scrollTop = evolutionLog.scrollHeight; | |
| } | |
| function escapeHtml(text) { | |
| const map = { | |
| '&': '&', | |
| '<': '<', | |
| '>': '>', | |
| '"': '"', | |
| "'": ''' | |
| }; | |
| return text.replace(/[&<>"']/g, function(m) { return map[m]; }); | |
| } | |
| // Initialize | |
| log('𧬠LLM Hill Climber ready!'); | |
| log('π€ Using StarCoder2-3B for code generation'); | |
| log('β‘ Single mutation strategy for API efficiency'); | |
| log('π Select a problem to begin evolution'); | |
| log(''); | |
| </script> | |
| </body> | |
| </html> |