mirror of
				https://github.com/open-webui/open-webui
				synced 2025-06-26 18:26:48 +00:00 
			
		
		
		
	feat: code execution time limit
This commit is contained in:
		
							parent
							
								
									c7221983a5
								
							
						
					
					
						commit
						a4630a9825
					
				| @ -139,73 +139,124 @@ | ||||
| 	}; | ||||
| 
 | ||||
| 	const executePython = async (code) => { | ||||
| 		if (!code.includes('input')) { | ||||
| 			executePythonAsWorker(code); | ||||
| 		} else { | ||||
| 			result = null; | ||||
| 			stdout = null; | ||||
| 			stderr = null; | ||||
| 
 | ||||
| 			executing = true; | ||||
| 
 | ||||
| 			let pyodide = await loadPyodide({ | ||||
| 				indexURL: '/pyodide/', | ||||
| 				stdout: (text) => { | ||||
| 					console.log('Python output:', text); | ||||
| 
 | ||||
| 					if (stdout) { | ||||
| 						stdout += `${text}\n`; | ||||
| 					} else { | ||||
| 						stdout = `${text}\n`; | ||||
| 					} | ||||
| 				}, | ||||
| 				stderr: (text) => { | ||||
| 					console.log('An error occured:', text); | ||||
| 					if (stderr) { | ||||
| 						stderr += `${text}\n`; | ||||
| 					} else { | ||||
| 						stderr = `${text}\n`; | ||||
| 					} | ||||
| 				} | ||||
| 			}); | ||||
| 
 | ||||
| 			try { | ||||
| 				const res = await pyodide.loadPackage('micropip'); | ||||
| 				console.log(res); | ||||
| 
 | ||||
| 				const micropip = pyodide.pyimport('micropip'); | ||||
| 
 | ||||
| 				await micropip.set_index_urls('https://pypi.org/pypi/{package_name}/json'); | ||||
| 
 | ||||
| 				let packages = [ | ||||
| 					code.includes('requests') ? 'requests' : null, | ||||
| 					code.includes('bs4') ? 'beautifulsoup4' : null, | ||||
| 					code.includes('numpy') ? 'numpy' : null, | ||||
| 					code.includes('pandas') ? 'pandas' : null | ||||
| 				].filter(Boolean); | ||||
| 
 | ||||
| 				console.log(packages); | ||||
| 				await micropip.install(packages); | ||||
| 
 | ||||
| 				result = await pyodide.runPythonAsync(`from js import prompt | ||||
| def input(p): | ||||
|     return prompt(p) | ||||
| __builtins__.input = input`); | ||||
| 
 | ||||
| 				result = await pyodide.runPython(code); | ||||
| 
 | ||||
| 				if (!result) { | ||||
| 					result = '[NO OUTPUT]'; | ||||
| 				} | ||||
| 
 | ||||
| 				console.log(result); | ||||
| 				console.log(stdout); | ||||
| 				console.log(stderr); | ||||
| 			} catch (error) { | ||||
| 				console.error('Error:', error); | ||||
| 				stderr = error; | ||||
| 			} | ||||
| 
 | ||||
| 			executing = false; | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	const executePythonAsWorker = async (code) => { | ||||
| 		result = null; | ||||
| 		stdout = null; | ||||
| 		stderr = null; | ||||
| 
 | ||||
| 		executing = true; | ||||
| 
 | ||||
| 		let pyodide = await loadPyodide({ | ||||
| 			indexURL: '/pyodide/', | ||||
| 			stdout: (text) => { | ||||
| 				console.log('Python output:', text); | ||||
| 		let packages = [ | ||||
| 			code.includes('requests') ? 'requests' : null, | ||||
| 			code.includes('bs4') ? 'beautifulsoup4' : null, | ||||
| 			code.includes('numpy') ? 'numpy' : null, | ||||
| 			code.includes('pandas') ? 'pandas' : null | ||||
| 		].filter(Boolean); | ||||
| 
 | ||||
| 				if (stdout) { | ||||
| 					stdout += `${text}\n`; | ||||
| 				} else { | ||||
| 					stdout = `${text}\n`; | ||||
| 				} | ||||
| 			}, | ||||
| 			stderr: (text) => { | ||||
| 				console.log('An error occured:', text); | ||||
| 				if (stderr) { | ||||
| 					stderr += `${text}\n`; | ||||
| 				} else { | ||||
| 					stderr = `${text}\n`; | ||||
| 				} | ||||
| 			} | ||||
| 		const pyodideWorker = new Worker('/pyodide-worker.js'); | ||||
| 
 | ||||
| 		pyodideWorker.postMessage({ | ||||
| 			id: id, | ||||
| 			code: code, | ||||
| 			packages: packages | ||||
| 		}); | ||||
| 
 | ||||
| 		try { | ||||
| 			const res = await pyodide.loadPackage('micropip'); | ||||
| 			console.log(res); | ||||
| 
 | ||||
| 			// pyodide.setStdin({ stdin: () => prompt() }); | ||||
| 
 | ||||
| 			const micropip = pyodide.pyimport('micropip'); | ||||
| 
 | ||||
| 			await micropip.set_index_urls('https://pypi.org/pypi/{package_name}/json'); | ||||
| 
 | ||||
| 			let packages = [ | ||||
| 				code.includes('requests') ? 'requests' : null, | ||||
| 				code.includes('bs4') ? 'beautifulsoup4' : null, | ||||
| 				code.includes('numpy') ? 'numpy' : null, | ||||
| 				code.includes('pandas') ? 'pandas' : null | ||||
| 			].filter(Boolean); | ||||
| 
 | ||||
| 			console.log(packages); | ||||
| 			await micropip.install(packages); | ||||
| 
 | ||||
| 			result = pyodide.runPython(`from js import prompt | ||||
| def input(p): | ||||
|     return prompt(p) | ||||
| __builtins__.input = input`); | ||||
| 
 | ||||
| 			result = pyodide.runPython(code); | ||||
| 
 | ||||
| 			if (!result) { | ||||
| 				result = '[NO OUTPUT]'; | ||||
| 		setTimeout(() => { | ||||
| 			if (executing) { | ||||
| 				executing = false; | ||||
| 				stderr = 'Execution Time Limit Exceeded'; | ||||
| 				pyodideWorker.terminate(); | ||||
| 			} | ||||
| 		}, 10000); | ||||
| 
 | ||||
| 			console.log(result); | ||||
| 			console.log(stdout); | ||||
| 			console.log(stderr); | ||||
| 		} catch (error) { | ||||
| 			console.error('Error:', error); | ||||
| 			stderr = error; | ||||
| 		} | ||||
| 		pyodideWorker.onmessage = (event) => { | ||||
| 			console.log('pyodideWorker.onmessage', event); | ||||
| 			const { id, ...data } = event.data; | ||||
| 
 | ||||
| 		executing = false; | ||||
| 			console.log(id, data); | ||||
| 
 | ||||
| 			data['stdout'] && (stdout = data['stdout']); | ||||
| 			data['stderr'] && (stderr = data['stderr']); | ||||
| 			data['result'] && (result = data['result']); | ||||
| 
 | ||||
| 			executing = false; | ||||
| 		}; | ||||
| 
 | ||||
| 		pyodideWorker.onerror = (event) => { | ||||
| 			console.log('pyodideWorker.onerror', event); | ||||
| 			executing = false; | ||||
| 		}; | ||||
| 	}; | ||||
| 
 | ||||
| 	$: highlightedCode = code ? hljs.highlightAuto(code, hljs.getLanguage(lang)?.aliases).value : ''; | ||||
|  | ||||
							
								
								
									
										55
									
								
								static/pyodide-worker.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								static/pyodide-worker.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | ||||
| // webworker.js
 | ||||
| // Setup your project to serve `py-worker.js`. You should also serve
 | ||||
| // `pyodide.js`, and all its associated `.asm.js`, `.json`,
 | ||||
| // and `.wasm` files as well:
 | ||||
| importScripts('/pyodide/pyodide.js'); | ||||
| 
 | ||||
| async function loadPyodideAndPackages(packages = []) { | ||||
| 	self.stdout = null; | ||||
| 	self.stderr = null; | ||||
| 	self.result = null; | ||||
| 
 | ||||
| 	self.pyodide = await loadPyodide({ | ||||
| 		indexURL: '/pyodide/', | ||||
| 		stdout: (text) => { | ||||
| 			console.log('Python output:', text); | ||||
| 
 | ||||
| 			if (self.stdout) { | ||||
| 				self.stdout += `${text}\n`; | ||||
| 			} else { | ||||
| 				self.stdout = `${text}\n`; | ||||
| 			} | ||||
| 		}, | ||||
| 		stderr: (text) => { | ||||
| 			console.log('An error occured:', text); | ||||
| 			if (self.stderr) { | ||||
| 				self.stderr += `${text}\n`; | ||||
| 			} else { | ||||
| 				self.stderr = `${text}\n`; | ||||
| 			} | ||||
| 		} | ||||
| 	}); | ||||
| 
 | ||||
| 	await self.pyodide.loadPackage('micropip'); | ||||
| 	const micropip = self.pyodide.pyimport('micropip'); | ||||
| 
 | ||||
| 	await micropip.set_index_urls('https://pypi.org/pypi/{package_name}/json'); | ||||
| 	await micropip.install(packages); | ||||
| } | ||||
| 
 | ||||
| self.onmessage = async (event) => { | ||||
| 	const { id, code, ...context } = event.data; | ||||
| 
 | ||||
| 	console.log(event.data) | ||||
| 
 | ||||
| 	// The worker copies the context in its own "memory" (an object mapping name to values)
 | ||||
| 	for (const key of Object.keys(context)) { | ||||
| 		self[key] = context[key]; | ||||
| 	} | ||||
| 
 | ||||
| 	// make sure loading is done
 | ||||
| 	await loadPyodideAndPackages(self.packages); | ||||
| 
 | ||||
| 	self.result = await self.pyodide.runPythonAsync(code); | ||||
| 	self.postMessage({ id, result: self.result, stdout: self.stdout, stderr: self.stderr }); | ||||
| }; | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user