eggie5-adyen commited on
Commit
6e648e8
0 Parent(s):

initial commit

Browse files
Files changed (5) hide show
  1. .gitignore +1 -0
  2. Dockerfile +24 -0
  3. README.md +10 -0
  4. app/jupyter_api.py +126 -0
  5. requirements.txt +5 -0
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ .venv
Dockerfile ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use an official Python runtime as a parent image
2
+ FROM python:3.9-slim
3
+
4
+ # Set environment variables
5
+ ENV PYTHONUNBUFFERED=1
6
+ ENV PYTHONIOENCODING=UTF-8
7
+
8
+ # Set working directory in the container
9
+ WORKDIR /app
10
+
11
+ # Copy the requirements file into the container
12
+ COPY requirements.txt /app/
13
+
14
+ # Install dependencies
15
+ RUN pip install --no-cache-dir -r requirements.txt
16
+
17
+ # Copy the FastAPI app code into the container
18
+ COPY . /app
19
+
20
+ # Expose the port FastAPI will run on
21
+ EXPOSE 7860
22
+
23
+ # Command to run the FastAPI app with Uvicorn
24
+ CMD ["uvicorn", "app.jupyter_api:app", "--host", "0.0.0.0", "--port", "7860"]
README.md ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Executor
3
+ emoji: 📚
4
+ colorFrom: blue
5
+ colorTo: green
6
+ sdk: docker
7
+ pinned: false
8
+ ---
9
+
10
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app/jupyter_api.py ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import uuid
2
+ from fastapi import FastAPI, HTTPException
3
+ from pydantic import BaseModel
4
+ from jupyter_client import KernelManager
5
+ from threading import Lock
6
+ import asyncio
7
+
8
+ app = FastAPI()
9
+
10
+ # A dictionary to store kernel sessions using session_id as the key
11
+ kernel_sessions = {}
12
+ # Lock for thread-safe access to the kernel_sessions dictionary
13
+ sessions_lock = Lock()
14
+
15
+ class CodeExecutionRequest(BaseModel):
16
+ code: str
17
+
18
+ class CreateSessionResponse(BaseModel):
19
+ session_id: str
20
+
21
+ @app.post("/create_session", response_model=CreateSessionResponse)
22
+ async def create_session():
23
+ """
24
+ Creates a new Jupyter kernel session and returns the session_id.
25
+ """
26
+ session_id = str(uuid.uuid4()) # Generate a unique session ID
27
+
28
+ with sessions_lock:
29
+ # Create a new kernel manager and start a kernel
30
+ km = KernelManager()
31
+ km.kernel_name = 'python3'
32
+ km.start_kernel()
33
+
34
+ # Create a client for interacting with the kernel
35
+ kc = km.client()
36
+ kc.start_channels()
37
+
38
+ # Store the kernel manager and client in the session dictionary
39
+ kernel_sessions[session_id] = {'km': km, 'kc': kc}
40
+
41
+ return CreateSessionResponse(session_id=session_id)
42
+
43
+ @app.post("/execute/{session_id}")
44
+ async def execute_code(session_id: str, request: CodeExecutionRequest):
45
+ """
46
+ Executes code in the specified session's Jupyter kernel.
47
+ """
48
+ with sessions_lock:
49
+ session = kernel_sessions.get(session_id)
50
+
51
+ if not session:
52
+ raise HTTPException(status_code=404, detail="Session not found")
53
+
54
+ kc = session['kc']
55
+
56
+ # Asynchronous code execution in the kernel
57
+ loop = asyncio.get_running_loop()
58
+ exec_id = uuid.uuid4().hex
59
+
60
+ # This function will run in a separate thread to avoid blocking
61
+ def run_code():
62
+ kc.execute(request.code)
63
+
64
+ # Collect output messages from the iopub channel
65
+ output = []
66
+ while True:
67
+ try:
68
+ msg = kc.get_iopub_msg(timeout=2)
69
+ msg_type = msg['msg_type']
70
+
71
+ # Process different types of iopub messages
72
+ if msg_type == 'stream':
73
+ output.append(msg['content']['text'])
74
+ elif msg_type == 'error':
75
+ # Include traceback if there's an error
76
+ output.extend(msg['content']['traceback'])
77
+ elif msg_type in ['execute_result', 'display_data']:
78
+ # Capture the output result if it exists
79
+ output.append(msg['content']['data'].get('text/plain', ''))
80
+
81
+ # Exit when execution completes
82
+ if msg_type == 'status' and msg['content']['execution_state'] == 'idle':
83
+ break
84
+
85
+ except Exception as e:
86
+ output.append(f"Error capturing output: {str(e)}")
87
+ break
88
+
89
+ return "\n".join(output)
90
+
91
+ try:
92
+ # Execute the code and await the result asynchronously
93
+ output = await loop.run_in_executor(None, run_code)
94
+ return {"status": "success", "output": output}
95
+ except Exception as e:
96
+ raise HTTPException(status_code=500, detail=str(e))
97
+
98
+ @app.post("/shutdown/{session_id}")
99
+ async def shutdown_session(session_id: str):
100
+ """
101
+ Shuts down the Jupyter kernel associated with the specified session_id.
102
+ """
103
+ with sessions_lock:
104
+ session = kernel_sessions.pop(session_id, None)
105
+
106
+ if not session:
107
+ raise HTTPException(status_code=404, detail="Session not found")
108
+
109
+ # Stop the kernel and clean up resources
110
+ try:
111
+ session['kc'].stop_channels()
112
+ session['km'].shutdown_kernel()
113
+ return {"status": "success", "message": "Session terminated"}
114
+ except Exception as e:
115
+ raise HTTPException(status_code=500, detail=str(e))
116
+
117
+ @app.get("/list_sessions")
118
+ async def list_sessions():
119
+ """
120
+ Lists all active Jupyter kernel sessions.
121
+ """
122
+ with sessions_lock:
123
+ # Prepare a list of session details
124
+ sessions_list = [{"session_id": sid} for sid in kernel_sessions.keys()]
125
+
126
+ return {"status": "success", "sessions": sessions_list}
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ ipykernel
2
+ pydantic
3
+ fastapi
4
+ uvicorn
5
+ jupyter_client