Team Ai
Apppublic

sanket3280/code-execution

sourceHugging Faceupdated 11mo agoView on Hugging Face
0likes
hackerEarthClient.js279 linesDownload Raw Back to execution
1const axios = require('axios');2 3const CODE_EVAL_URL = process.env.CODE_EVAL_URL || 'https://api.hackerearth.com/v4/partner/code-evaluation/submissions/';4const CLIENT_SECRET = process.env.CLIENT_SECRET;5 6const STATUS_CODES = {7  'AC': 'Accepted',8  'TLE': 'Time Limit Exceeded',9  'MLE': 'Memory Limit Exceeded',10  'RE': 'Runtime Error',11  'CE': 'Compilation Error',12  'REQUEST_FAILED': 'Request Failed'13};14 15/**16 * Normalize output for comparison - handles arrays, objects, strings, numbers17 * Ensures consistent comparison regardless of data type18 */19const normalizeOutputForCompare = (text) => {20  if (text === null || text === undefined) return '';21  22  let str = String(text).trim();23  24  // Remove all whitespace for comparison25  str = str.replace(/\s+/g, '');26  27  // Remove quotes if present (to compare "321" with 321)28  str = str.replace(/^["']|["']$/g, '');29  30  // Handle boolean values31  const lower = str.toLowerCase();32  if (lower === 'true') return 'true';33  if (lower === 'false') return 'false';34  35  // Try to parse as number36  const num = Number(str);37  if (!isNaN(num) && str !== '') {38    return String(num); // Convert to string for consistent comparison39  }40  41  // Try to parse as JSON for arrays/objects42  try {43    const parsed = JSON.parse(str);44    // Stringify back to ensure consistent format45    return JSON.stringify(parsed).replace(/\s+/g, '');46  } catch (e) {47    // Not JSON, return as is (without whitespace and quotes)48    return str;49  }50};51 52/**53 * Get output content from URL or direct string54 */55const getOutputContent = async (outputUrl) => {56  try {57    // If it's already a string (Docker output), return it directly58    if (typeof outputUrl === 'string' && !outputUrl.startsWith('http')) {59      return outputUrl.trim();60    }61    62    // Otherwise fetch from URL (HackerEarth output)63    const response = await axios.get(outputUrl, { 64      responseType: 'text',65      timeout: 10000 66    });67    let output = '';68    if (typeof response.data === 'string') output = response.data;69    else if (typeof response.data === 'number') output = String(response.data);70    else if (response.data && typeof response.data === 'object') output = JSON.stringify(response.data);71    else output = String(response.data || '');72    return output.trim();73  } catch (error) {74    console.error('โŒ Error fetching output content:', error.message);75    return '';76  }77};78 79/**80 * Extract output from run status81 */82const extractOutput = async (runStatus) => {83  try {84    if (!runStatus) return '';85 86    if (runStatus.status === 'CODE_COMPILED' && !runStatus.output && !runStatus.stdout) {87      return '';88    }89 90 91    if (runStatus.output) {92      const output = await getOutputContent(runStatus.output);93      if (output && output.trim()) return output;94    }95    if (runStatus.output_html) return String(runStatus.output_html).replace(/<[^>]+>/g, '').trim();96    if (runStatus.stdout) return String(runStatus.stdout).trim();97    if (runStatus.compile_status && runStatus.status === 'CE') return String(runStatus.compile_status).trim();98    if (runStatus.stderr) return String(runStatus.stderr).trim();99 100    if (runStatus.result && runStatus.result.run_status) {101      const r = runStatus.result.run_status;102      if (r.output) return r.output;103      if (r.stdout) return r.stdout;104      if (r.stderr) return r.stderr;105    }106  } catch (e) {107    console.error('extractOutput error:', e);108    return '';109  }110  return '';111};112 113// In-memory store for Docker results (temporary, until proper DB storage)114const dockerResults = new Map();115 116/**117 * Store Docker result for later retrieval118 */119const storeDockerResult = (heId, result) => {120  dockerResults.set(heId, result);121  // Auto-cleanup after 5 minutes122  setTimeout(() => dockerResults.delete(heId), 5 * 60 * 1000);123};124 125/**126 * Get status of submission127 */128const getStatus = async (heId) => {129  // Check if this is a Docker result130  if (heId && heId.startsWith('docker_')) {131    const result = dockerResults.get(heId);132    if (result) {133      return result;134    }135    throw new Error('Docker result not found or expired');136  }137  138  // HackerEarth result139  if (!CLIENT_SECRET) throw new Error('HackerEarth CLIENT_SECRET not configured');140  const url = `${CODE_EVAL_URL}${heId}/`;141  const headers = { 'client-secret': CLIENT_SECRET };142  const response = await axios.get(url, { headers, timeout: 10000 });143  return response.data;144};145 146/**147 * Poll for completion with optimized polling intervals148 */149const pollForCompletion = async (heId, maxAttempts = 10, interval = 100) => {150  // Check if this is a Docker result (heId starts with "docker_")151  if (heId && heId.startsWith('docker_')) {152    console.log('\n' + '='.repeat(80));153    console.log('๐Ÿณ DOCKER RESULT - No polling needed');154    console.log('='.repeat(80) + '\n');155    156    // Docker result is already complete, fetch it directly157    try {158      const status = await getStatus(heId);159      return status;160    } catch (error) {161      // If getStatus fails for Docker, it means result is not stored162      // This shouldn't happen, but handle gracefully163      console.error('โŒ Failed to get Docker result:', error.message);164      throw new Error('Docker result not found');165    }166  }167  168  let attempts = 0;169  let codeCompiledCounter = 0;170  let firstCodeCompiledAt = null;171  172  // Optimized polling for HackerEarth173  const optimizedInterval = 200;174  const optimizedMaxAttempts = 20;175 176  while (attempts < optimizedMaxAttempts) {177    try {178      const status = await getStatus(heId);179      const runStatus = status.result?.run_status;180      const requestStatus = status.request_status?.code;181 182      if (requestStatus === 'REQUEST_FAILED') {183        throw new Error('Code execution failed');184      }185 186      // Track CODE_COMPILED state - Be patient with HackerEarth187      if (runStatus?.status === 'CODE_COMPILED') {188        if (firstCodeCompiledAt === null) {189          firstCodeCompiledAt = attempts;190        }191        codeCompiledCounter++;192        193        // Allow 15 attempts (3 seconds) before considering it stuck194        // HackerEarth needs time for Java compilation + execution195        if (codeCompiledCounter >= 15) {196          // Don't throw error, just return the status and let it be handled197          console.log('โš ๏ธ Still CODE_COMPILED after 15 attempts, returning status');198          return status;199        }200      } else {201        codeCompiledCounter = 0;202        firstCodeCompiledAt = null;203      }204 205      if (requestStatus === 'REQUEST_COMPLETED' && runStatus) {206        const finalStatuses = ['AC', 'TLE', 'MLE', 'RE', 'CE', 'WA'];207        208        if (runStatus.status === 'NA' || runStatus.status === 'REQUEST_QUEUED') {209          // Continue polling210        } else if (runStatus.status === 'CODE_COMPILED') {211          // CODE_COMPILED means compilation succeeded but no execution output yet212          // Continue polling unless we've waited too long213          if (codeCompiledCounter >= 15) {214            return status; // Return what we have215          }216        } else if (finalStatuses.includes(runStatus.status)) {217          return status;218        } else {219          return status;220        }221      }222 223      await new Promise(r => setTimeout(r, optimizedInterval));224      attempts++;225    } catch (error) {226      attempts++;227      if (attempts >= optimizedMaxAttempts) throw error;228    }229  }230  231  try {232    const finalStatus = await getStatus(heId);233    return finalStatus;234  } catch (e) {235    throw new Error(`Timeout after ${optimizedMaxAttempts} attempts`);236  }237};238 239/**240 * Submit code to HackerEarth with detailed logging241 */242const submitToHackerEarth = async ({ source, language, memoryLimit = 131072, timeLimit = 3, input, usedRunner }) => {243  if (!CLIENT_SECRET) {244    throw new Error('HackerEarth CLIENT_SECRET not configured. Please set CLIENT_SECRET environment variable.');245  }246  247  const headers = { 248    'client-secret': CLIENT_SECRET, 249    'content-type': 'application/json' 250  };251  252  const data = {253    lang: language,254    source,255    memory_limit: memoryLimit,256    time_limit: timeLimit,257    context: JSON.stringify({ id: Date.now() })258  };259  260  if (!usedRunner) data.input = input;261  262  const response = await axios.post(CODE_EVAL_URL, data, { 263    headers, 264    timeout: 10000 265  });266  267  return response.data;268};269 270module.exports = {271  STATUS_CODES,272  normalizeOutputForCompare,273  getOutputContent,274  extractOutput,275  getStatus,276  storeDockerResult,277  pollForCompletion,278  submitToHackerEarth279};