code_interpreter.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import asyncio
  2. import json
  3. import uuid
  4. import websockets
  5. import requests
  6. from urllib.parse import urljoin
  7. async def execute_code_jupyter(
  8. jupyter_url, code, token=None, password=None, timeout=10
  9. ):
  10. """
  11. Executes Python code in a Jupyter kernel.
  12. Supports authentication with a token or password.
  13. :param jupyter_url: Jupyter server URL (e.g., "http://localhost:8888")
  14. :param code: Code to execute
  15. :param token: Jupyter authentication token (optional)
  16. :param password: Jupyter password (optional)
  17. :param timeout: WebSocket timeout in seconds (default: 10s)
  18. :return: Dictionary with stdout, stderr, and result
  19. - Images are prefixed with "base64:image/png," and separated by newlines if multiple.
  20. """
  21. session = requests.Session() # Maintain cookies
  22. headers = {} # Headers for requests
  23. # Authenticate using password
  24. if password and not token:
  25. try:
  26. login_url = urljoin(jupyter_url, "/login")
  27. response = session.get(login_url)
  28. response.raise_for_status()
  29. xsrf_token = session.cookies.get("_xsrf")
  30. if not xsrf_token:
  31. raise ValueError("Failed to fetch _xsrf token")
  32. login_data = {"_xsrf": xsrf_token, "password": password}
  33. login_response = session.post(
  34. login_url, data=login_data, cookies=session.cookies
  35. )
  36. login_response.raise_for_status()
  37. headers["X-XSRFToken"] = xsrf_token
  38. except Exception as e:
  39. return {
  40. "stdout": "",
  41. "stderr": f"Authentication Error: {str(e)}",
  42. "result": "",
  43. }
  44. # Construct API URLs with authentication token if provided
  45. params = f"?token={token}" if token else ""
  46. kernel_url = urljoin(jupyter_url, f"/api/kernels{params}")
  47. try:
  48. response = session.post(kernel_url, headers=headers, cookies=session.cookies)
  49. response.raise_for_status()
  50. kernel_id = response.json()["id"]
  51. websocket_url = urljoin(
  52. jupyter_url.replace("http", "ws"),
  53. f"/api/kernels/{kernel_id}/channels{params}",
  54. )
  55. ws_headers = {}
  56. if password and not token:
  57. ws_headers["X-XSRFToken"] = session.cookies.get("_xsrf")
  58. cookies = {name: value for name, value in session.cookies.items()}
  59. ws_headers["Cookie"] = "; ".join(
  60. [f"{name}={value}" for name, value in cookies.items()]
  61. )
  62. async with websockets.connect(
  63. websocket_url, additional_headers=ws_headers
  64. ) as ws:
  65. msg_id = str(uuid.uuid4())
  66. execute_request = {
  67. "header": {
  68. "msg_id": msg_id,
  69. "msg_type": "execute_request",
  70. "username": "user",
  71. "session": str(uuid.uuid4()),
  72. "date": "",
  73. "version": "5.3",
  74. },
  75. "parent_header": {},
  76. "metadata": {},
  77. "content": {
  78. "code": code,
  79. "silent": False,
  80. "store_history": True,
  81. "user_expressions": {},
  82. "allow_stdin": False,
  83. "stop_on_error": True,
  84. },
  85. "channel": "shell",
  86. }
  87. await ws.send(json.dumps(execute_request))
  88. stdout, stderr, result = "", "", []
  89. while True:
  90. try:
  91. message = await asyncio.wait_for(ws.recv(), timeout)
  92. message_data = json.loads(message)
  93. if message_data.get("parent_header", {}).get("msg_id") == msg_id:
  94. msg_type = message_data.get("msg_type")
  95. if msg_type == "stream":
  96. if message_data["content"]["name"] == "stdout":
  97. stdout += message_data["content"]["text"]
  98. elif message_data["content"]["name"] == "stderr":
  99. stderr += message_data["content"]["text"]
  100. elif msg_type in ("execute_result", "display_data"):
  101. data = message_data["content"]["data"]
  102. if "image/png" in data:
  103. result.append(
  104. f"data:image/png;base64,{data['image/png']}"
  105. )
  106. elif "text/plain" in data:
  107. result.append(data["text/plain"])
  108. elif msg_type == "error":
  109. stderr += "\n".join(message_data["content"]["traceback"])
  110. elif (
  111. msg_type == "status"
  112. and message_data["content"]["execution_state"] == "idle"
  113. ):
  114. break
  115. except asyncio.TimeoutError:
  116. stderr += "\nExecution timed out."
  117. break
  118. except Exception as e:
  119. return {"stdout": "", "stderr": f"Error: {str(e)}", "result": ""}
  120. finally:
  121. if kernel_id:
  122. requests.delete(
  123. f"{kernel_url}/{kernel_id}", headers=headers, cookies=session.cookies
  124. )
  125. return {
  126. "stdout": stdout.strip(),
  127. "stderr": stderr.strip(),
  128. "result": "\n".join(result).strip() if result else "",
  129. }