greed.js
Version:
Lightweight, private alternative to Colab. Run PyTorch & NumPy in browser with GPU acceleration (8.8x speedup). Fast, secure, runs locally.
1 lines • 9.55 kB
JavaScript
(()=>{class e{constructor(){this.stats={executionCount:0,lastCleanup:Date.now(),memoryPressure:0,avgExecutionTime:0},this.config={cleanupInterval:100,memoryThreshold:.8,maxIdleTime:3e5,preserveGlobals:new Set(["torch","nn","optim","numpy","np","sys","os","math","gc","builtins","__builtins__","__name__","__doc__","__package__","__loader__","__spec__","__annotations__","model","optimizer","loss_fn","dataset","dataloader","train","test","val","X","y","data","losses","accuracies","epochs","history","results"])},this.userContext=new Map,this.lastAccessTime=Date.now()}preserve(e){this.config.preserveGlobals.add(e)}shouldPreserve(e){if(this.config.preserveGlobals.has(e))return!0;const t=this.userContext.get(e);return!!(t&&Date.now()-t<this.config.maxIdleTime)}accessed(e){this.userContext.set(e,Date.now()),this.lastAccessTime=Date.now()}recordExecution(e){this.stats.executionCount++,this.stats.avgExecutionTime=(this.stats.avgExecutionTime*(this.stats.executionCount-1)+e)/this.stats.executionCount}needsCleanup(){return this.stats.executionCount%this.config.cleanupInterval===0||(this.stats.memoryPressure>this.config.memoryThreshold||Date.now()-this.lastAccessTime>this.config.maxIdleTime)}getCleanupCode(){return`\nimport gc\nimport sys\npreserved_globals = {"${Array.from(this.config.preserveGlobals).join('", "')}"}\ncurrent_globals = list(globals().keys())\ndeleted_count = 0\nfor var_name in current_globals:\n if var_name in preserved_globals:\n continue\n if var_name.startswith('__') and var_name.endswith('__'):\n continue\n if var_name in sys.modules:\n continue\n if var_name.startswith('_temp_') or var_name.startswith('_out_'):\n try:\n del globals()[var_name]\n deleted_count += 1\n except:\n pass\ngc.collect()\n_cleanup_stats = {'deleted': deleted_count, 'memory_freed': True}\n`}getMemoryOptimizationCode(){return"\nimport gc\nimport sys\ndef _cleanup_tensors():\n try:\n import torch\n if hasattr(torch, 'cuda') and torch.cuda.is_available():\n torch.cuda.empty_cache()\n for obj in gc.get_objects():\n if isinstance(obj, torch.Tensor) and obj.grad is not None:\n if not obj.requires_grad:\n obj.grad = None\n except:\n pass\n_cleanup_tensors()\ngc.collect(generation=2)\n_memory_optimized = True\n"}getStats(){return{...this.stats,idleTime:Date.now()-this.lastAccessTime,preservedVariables:Array.from(this.config.preserveGlobals)}}reset(){this.stats={executionCount:0,lastCleanup:Date.now(),memoryPressure:0,avgExecutionTime:0},this.userContext.clear(),this.lastAccessTime=Date.now()}}let t=null,s=!1,a=new Set,n=null,r=null;self.onmessage=async function(o){const{type:i,id:c,...l}=o.data;try{switch(i){case"init":await async function(o){return n||(n=(async()=>{try{return importScripts(o.pyodideURL||"https://cdn.jsdelivr.net/pyodide/v0.24.1/full/pyodide.js"),postMessage({type:"init:progress",stage:"loading",message:"Loading Pyodide..."}),t=await loadPyodide({indexURL:o.indexURL||"https://cdn.jsdelivr.net/pyodide/v0.24.1/full/"}),postMessage({type:"init:progress",stage:"loaded",message:"Pyodide loaded successfully"}),o.preloadPackages&&o.preloadPackages.length>0&&(postMessage({type:"init:progress",stage:"packages",message:`Loading packages: ${o.preloadPackages.join(", ")}`}),await t.loadPackage(o.preloadPackages),o.preloadPackages.forEach(e=>a.add(e)),postMessage({type:"init:progress",stage:"packages-loaded",message:`Packages loaded: ${o.preloadPackages.join(", ")}`})),r=new e,s=!0,postMessage({type:"init:complete",installedPackages:Array.from(a)}),!0}catch(e){throw postMessage({type:"init:error",error:{message:e.message,stack:e.stack}}),e}})(),n)}(l.config),postMessage({type:"init:ack",id:c});break;case"loadPackages":await async function(e){if(!s)throw new Error("Pyodide not initialized");const n=e.filter(e=>!a.has(e));if(0===n.length)return Array.from(a);try{return postMessage({type:"packages:loading",packages:n}),await t.loadPackage(n),n.forEach(e=>a.add(e)),postMessage({type:"packages:loaded",packages:n,allPackages:Array.from(a)}),Array.from(a)}catch(e){throw postMessage({type:"packages:error",error:{message:e.message,packages:n}}),e}}(l.packages),postMessage({type:"loadPackages:ack",id:c,packages:Array.from(a)});break;case"execute":await async function(e,a,n={}){if(!s)throw new Error("Pyodide not initialized");const{captureOutput:o=!1,globals:i={},validateInput:c=!0}=n,l=performance.now();try{for(const[s,a]of Object.entries(i))try{t.globals.set(s,a),r.accessed(s)}catch(t){postMessage({type:"execution:warning",taskId:e,warning:`Failed to set global '${s}': ${t.message}`})}let s;if(o){const r=!1!==n.streamOutput;r&&e&&t.globals.set("__greed_worker_emit_stdout__",(e,t)=>{postMessage({type:"execution:stdout",taskId:e,output:t,timestamp:Date.now()})});const o=`\nimport sys\nfrom io import StringIO\n\nclass StreamingBuffer:\n def __init__(self, task_id, emit_callback, should_stream):\n self.buffer = StringIO()\n self.task_id = task_id\n self.emit_callback = emit_callback\n self.should_stream = should_stream\n\n def write(self, text):\n self.buffer.write(text)\n # Emit immediately for real-time streaming\n if self.should_stream and self.emit_callback and text:\n # Emit immediately - don't wait for time intervals\n # This ensures output appears in real-time, even during sleep() calls\n self.emit_callback(self.task_id, text)\n\n def flush(self):\n self.buffer.flush()\n\n def getvalue(self):\n return self.buffer.getvalue()\n\n_temp_output_buffer = StreamingBuffer('${e||""}', ${r?"__greed_worker_emit_stdout__":"None"}, ${r?"True":"False"})\n_temp_original_stdout = sys.stdout\nsys.stdout = _temp_output_buffer\n\ntry:\n${a.split("\n").map(e=>" "+e).join("\n")}\nfinally:\n sys.stdout.flush()\n sys.stdout = _temp_original_stdout\n _temp_captured_output = _temp_output_buffer.getvalue()\n # Debug: also store in a backup variable\n _temp_backup_output = _temp_captured_output\n`;await t.runPythonAsync(o);let i="";try{i=t.globals.get("_temp_captured_output")||"";const s=t.globals.get("_temp_backup_output")||"",a=t.runPython("_temp_output_buffer.getvalue()");postMessage({type:"execution:warning",taskId:e,warning:`[Worker] _temp_captured_output: "${i}" (len: ${i.length}), backup: "${s}" (len: ${s.length}), buffer: "${a}" (len: ${a.length})`})}catch(t){i="Output capture failed",postMessage({type:"execution:warning",taskId:e,warning:`[Worker] Capture failed: ${t.message}`})}s={output:i},postMessage({type:"execution:warning",taskId:e,warning:`[Worker] Result object: ${JSON.stringify(s)}`});try{t.globals.delete("_temp_captured_output"),t.globals.delete("_temp_output_buffer"),t.globals.delete("_temp_original_stdout")}catch(e){}}else s=await t.runPythonAsync(a);const c=performance.now()-l;if(r.recordExecution(c),r.needsCleanup())try{await t.runPythonAsync(r.getCleanupCode());const s=r.getStats();postMessage({type:"execution:cleanup",taskId:e,stats:JSON.parse(JSON.stringify(s))})}catch(t){postMessage({type:"execution:warning",taskId:e,warning:`Cleanup warning: ${t.message}`})}if(r.stats.executionCount%50==0)try{await t.runPythonAsync(r.getMemoryOptimizationCode())}catch(e){}let u=s;if(s&&"object"==typeof s&&s.toJs)try{u=s.toJs({dict_converter:Object.fromEntries})}catch(e){u=s.toString()}return postMessage({type:"execution:complete",taskId:e,result:u,stats:{duration:c,executionCount:r.stats.executionCount}}),s}catch(t){throw postMessage({type:"execution:error",taskId:e,error:{message:t.message,stack:t.stack,type:t.constructor.name}}),t}}(l.taskId,l.code,l.options),postMessage({type:"execute:ack",id:c,taskId:l.taskId});break;case"getGlobal":const o=function(e){if(!s)throw new Error("Pyodide not initialized");try{return t.globals.get(e)}catch(e){return}}(l.name);postMessage({type:"getGlobal:result",id:c,name:l.name,value:o});break;case"setGlobal":const u=function(e,a){if(!s)throw new Error("Pyodide not initialized");try{return t.globals.set(e,a),!0}catch(e){return!1}}(l.name,l.value);postMessage({type:"setGlobal:result",id:c,name:l.name,success:u});break;case"deleteGlobal":const p=function(e){if(!s)throw new Error("Pyodide not initialized");try{return t.globals.delete(e),!0}catch(e){return!1}}(l.name);postMessage({type:"deleteGlobal:result",id:c,name:l.name,success:p});break;case"interrupt":!function(){if(s&&t)try{t.interruptBuffer[0]=2,postMessage({type:"execution:interrupted",message:"Execution interrupted by user"})}catch(e){postMessage({type:"interrupt:error",error:{message:e.message}})}}(),postMessage({type:"interrupt:ack",id:c});break;case"reset":await async function(){if(s)try{await t.runPythonAsync("\nimport sys\n# Get all user-defined names\nuser_names = [name for name in dir() if not name.startswith('_')]\n# Delete them\nfor name in user_names:\n try:\n del globals()[name]\n except:\n pass\n"),postMessage({type:"reset:complete",message:"Python environment reset"})}catch(e){postMessage({type:"reset:error",error:{message:e.message}})}}(),postMessage({type:"reset:ack",id:c});break;case"ping":postMessage({type:"pong",id:c});break;default:postMessage({type:"error",id:c,error:{message:`Unknown message type: ${i}`}})}}catch(e){postMessage({type:"error",id:c,error:{message:e.message,stack:e.stack}})}};try{postMessage({type:"worker:ready"})}catch(e){console.error("Worker failed to send ready message:",e)}})();