__init__.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import subprocess
  2. import time
  3. from dataclasses import dataclass
  4. from openhands.core.logger import openhands_logger as logger
  5. from openhands.events.action import Action, IPythonRunCellAction
  6. from openhands.events.observation import IPythonRunCellObservation
  7. from openhands.runtime.plugins.jupyter.execute_server import JupyterKernel
  8. from openhands.runtime.plugins.requirement import Plugin, PluginRequirement
  9. from openhands.runtime.utils import find_available_tcp_port
  10. from openhands.utils.shutdown_listener import should_continue
  11. @dataclass
  12. class JupyterRequirement(PluginRequirement):
  13. name: str = 'jupyter'
  14. class JupyterPlugin(Plugin):
  15. name: str = 'jupyter'
  16. async def initialize(self, username: str, kernel_id: str = 'openhands-default'):
  17. self.kernel_gateway_port = find_available_tcp_port(40000, 49999)
  18. self.kernel_id = kernel_id
  19. self.gateway_process = subprocess.Popen(
  20. (
  21. f"su - {username} -s /bin/bash << 'EOF'\n"
  22. 'cd /openhands/code\n'
  23. 'export POETRY_VIRTUALENVS_PATH=/openhands/poetry;\n'
  24. 'export PYTHONPATH=/openhands/code:$PYTHONPATH;\n'
  25. 'export MAMBA_ROOT_PREFIX=/openhands/micromamba;\n'
  26. '/openhands/micromamba/bin/micromamba run -n openhands '
  27. 'poetry run jupyter kernelgateway '
  28. '--KernelGatewayApp.ip=0.0.0.0 '
  29. f'--KernelGatewayApp.port={self.kernel_gateway_port}\n'
  30. 'EOF'
  31. ),
  32. stderr=subprocess.STDOUT,
  33. shell=True,
  34. )
  35. # read stdout until the kernel gateway is ready
  36. output = ''
  37. while should_continue() and self.gateway_process.stdout is not None:
  38. line = self.gateway_process.stdout.readline().decode('utf-8')
  39. output += line
  40. if 'at' in line:
  41. break
  42. time.sleep(1)
  43. logger.debug('Waiting for jupyter kernel gateway to start...')
  44. logger.debug(
  45. f'Jupyter kernel gateway started at port {self.kernel_gateway_port}. Output: {output}'
  46. )
  47. _obs = await self.run(
  48. IPythonRunCellAction(code='import sys; print(sys.executable)')
  49. )
  50. self.python_interpreter_path = _obs.content.strip()
  51. async def _run(self, action: Action) -> IPythonRunCellObservation:
  52. """Internal method to run a code cell in the jupyter kernel."""
  53. if not isinstance(action, IPythonRunCellAction):
  54. raise ValueError(
  55. f'Jupyter plugin only supports IPythonRunCellAction, but got {action}'
  56. )
  57. if not hasattr(self, 'kernel'):
  58. self.kernel = JupyterKernel(
  59. f'localhost:{self.kernel_gateway_port}', self.kernel_id
  60. )
  61. if not self.kernel.initialized:
  62. await self.kernel.initialize()
  63. output = await self.kernel.execute(action.code, timeout=action.timeout)
  64. return IPythonRunCellObservation(
  65. content=output,
  66. code=action.code,
  67. )
  68. async def run(self, action: Action) -> IPythonRunCellObservation:
  69. obs = await self._run(action)
  70. return obs