RapidOrc121 commited on
Commit
7ed834b
ยท
verified ยท
1 Parent(s): db43ea1

Upload folder using huggingface_hub

Browse files
Files changed (5) hide show
  1. Dockerfile +8 -0
  2. README.md +255 -255
  3. server/_notebook.html +40 -40
  4. server/custom_ui.py +53 -66
  5. uv.lock +0 -0
Dockerfile CHANGED
@@ -54,6 +54,14 @@ RUN --mount=type=cache,target=/root/.cache/uv \
54
  uv sync --no-editable; \
55
  fi
56
 
 
 
 
 
 
 
 
 
57
  # Final runtime stage
58
  FROM ${BASE_IMAGE}
59
 
 
54
  uv sync --no-editable; \
55
  fi
56
 
57
+ # Explicitly install notebook rendering + model inference deps.
58
+ # These may not be in the base image or uv.lock on first deploy.
59
+ RUN .venv/bin/pip install --quiet \
60
+ nbconvert>=7.0.0 \
61
+ nbformat>=5.0.0 \
62
+ transformers>=4.40.0 \
63
+ accelerate>=0.29.0
64
+
65
  # Final runtime stage
66
  FROM ${BASE_IMAGE}
67
 
README.md CHANGED
@@ -1,255 +1,255 @@
1
- ---
2
- title: Incident Response Env Environment Server
3
- emoji: ๐ŸŽฃ
4
- colorFrom: red
5
- colorTo: pink
6
- sdk: docker
7
- pinned: false
8
- app_port: 8000
9
- base_path: /web
10
- tags:
11
- - openenv
12
- ---
13
-
14
- # Incident Response Env Environment
15
-
16
- A simple test environment that echoes back messages. Perfect for testing the env APIs as well as demonstrating environment usage patterns.
17
-
18
- ## Quick Start
19
-
20
- The simplest way to use the Incident Response Env environment is through the `IncidentResponseEnv` class:
21
-
22
- ```python
23
- from incident_response_env import IncidentResponseAction, IncidentResponseEnv
24
-
25
- try:
26
- # Create environment from Docker image
27
- incident_response_envenv = IncidentResponseEnv.from_docker_image("incident_response_env-env:latest")
28
-
29
- # Reset
30
- result = incident_response_envenv.reset()
31
- print(f"Reset: {result.observation.echoed_message}")
32
-
33
- # Send multiple messages
34
- messages = ["Hello, World!", "Testing echo", "Final message"]
35
-
36
- for msg in messages:
37
- result = incident_response_envenv.step(IncidentResponseAction(message=msg))
38
- print(f"Sent: '{msg}'")
39
- print(f" โ†’ Echoed: '{result.observation.echoed_message}'")
40
- print(f" โ†’ Length: {result.observation.message_length}")
41
- print(f" โ†’ Reward: {result.reward}")
42
-
43
- finally:
44
- # Always clean up
45
- incident_response_envenv.close()
46
- ```
47
-
48
- That's it! The `IncidentResponseEnv.from_docker_image()` method handles:
49
- - Starting the Docker container
50
- - Waiting for the server to be ready
51
- - Connecting to the environment
52
- - Container cleanup when you call `close()`
53
-
54
- ## Building the Docker Image
55
-
56
- Before using the environment, you need to build the Docker image:
57
-
58
- ```bash
59
- # From project root
60
- docker build -t incident_response_env-env:latest -f server/Dockerfile .
61
- ```
62
-
63
- ## Deploying to Hugging Face Spaces
64
-
65
- You can easily deploy your OpenEnv environment to Hugging Face Spaces using the `openenv push` command:
66
-
67
- ```bash
68
- # From the environment directory (where openenv.yaml is located)
69
- openenv push
70
-
71
- # Or specify options
72
- openenv push --namespace my-org --private
73
- ```
74
-
75
- The `openenv push` command will:
76
- 1. Validate that the directory is an OpenEnv environment (checks for `openenv.yaml`)
77
- 2. Prepare a custom build for Hugging Face Docker space (enables web interface)
78
- 3. Upload to Hugging Face (ensuring you're logged in)
79
-
80
- ### Prerequisites
81
-
82
- - Authenticate with Hugging Face: The command will prompt for login if not already authenticated
83
-
84
- ### Options
85
-
86
- - `--directory`, `-d`: Directory containing the OpenEnv environment (defaults to current directory)
87
- - `--repo-id`, `-r`: Repository ID in format 'username/repo-name' (defaults to 'username/env-name' from openenv.yaml)
88
- - `--base-image`, `-b`: Base Docker image to use (overrides Dockerfile FROM)
89
- - `--private`: Deploy the space as private (default: public)
90
-
91
- ### Examples
92
-
93
- ```bash
94
- # Push to your personal namespace (defaults to username/env-name from openenv.yaml)
95
- openenv push
96
-
97
- # Push to a specific repository
98
- openenv push --repo-id my-org/my-env
99
-
100
- # Push with a custom base image
101
- openenv push --base-image ghcr.io/meta-pytorch/openenv-base:latest
102
-
103
- # Push as a private space
104
- openenv push --private
105
-
106
- # Combine options
107
- openenv push --repo-id my-org/my-env --base-image custom-base:latest --private
108
- ```
109
-
110
- After deployment, your space will be available at:
111
- `https://huggingface.co/spaces/<repo-id>`
112
-
113
- The deployed space includes:
114
- - **Web Interface** at `/web` - Interactive UI for exploring the environment
115
- - **API Documentation** at `/docs` - Full OpenAPI/Swagger interface
116
- - **Health Check** at `/health` - Container health monitoring
117
- - **WebSocket** at `/ws` - Persistent session endpoint for low-latency interactions
118
-
119
- ## Environment Details
120
-
121
- ### Action
122
- **IncidentResponseAction**: Contains a single field
123
- - `message` (str) - The message to echo back
124
-
125
- ### Observation
126
- **IncidentResponseObservation**: Contains the echo response and metadata
127
- - `echoed_message` (str) - The message echoed back
128
- - `message_length` (int) - Length of the message
129
- - `reward` (float) - Reward based on message length (length ร— 0.1)
130
- - `done` (bool) - Always False for echo environment
131
- - `metadata` (dict) - Additional info like step count
132
-
133
- ### Reward
134
- The reward is calculated as: `message_length ร— 0.1`
135
- - "Hi" โ†’ reward: 0.2
136
- - "Hello, World!" โ†’ reward: 1.3
137
- - Empty message โ†’ reward: 0.0
138
-
139
- ## Advanced Usage
140
-
141
- ### Connecting to an Existing Server
142
-
143
- If you already have a Incident Response Env environment server running, you can connect directly:
144
-
145
- ```python
146
- from incident_response_env import IncidentResponseEnv
147
-
148
- # Connect to existing server
149
- incident_response_envenv = IncidentResponseEnv(base_url="<ENV_HTTP_URL_HERE>")
150
-
151
- # Use as normal
152
- result = incident_response_envenv.reset()
153
- result = incident_response_envenv.step(IncidentResponseAction(message="Hello!"))
154
- ```
155
-
156
- Note: When connecting to an existing server, `incident_response_envenv.close()` will NOT stop the server.
157
-
158
- ### Using the Context Manager
159
-
160
- The client supports context manager usage for automatic connection management:
161
-
162
- ```python
163
- from incident_response_env import IncidentResponseAction, IncidentResponseEnv
164
-
165
- # Connect with context manager (auto-connects and closes)
166
- with IncidentResponseEnv(base_url="http://localhost:8000") as env:
167
- result = env.reset()
168
- print(f"Reset: {result.observation.echoed_message}")
169
- # Multiple steps with low latency
170
- for msg in ["Hello", "World", "!"]:
171
- result = env.step(IncidentResponseAction(message=msg))
172
- print(f"Echoed: {result.observation.echoed_message}")
173
- ```
174
-
175
- The client uses WebSocket connections for:
176
- - **Lower latency**: No HTTP connection overhead per request
177
- - **Persistent session**: Server maintains your environment state
178
- - **Efficient for episodes**: Better for many sequential steps
179
-
180
- ### Concurrent WebSocket Sessions
181
-
182
- The server supports multiple concurrent WebSocket connections. To enable this,
183
- modify `server/app.py` to use factory mode:
184
-
185
- ```python
186
- # In server/app.py - use factory mode for concurrent sessions
187
- app = create_app(
188
- IncidentResponseEnvironment, # Pass class, not instance
189
- IncidentResponseAction,
190
- IncidentResponseObservation,
191
- max_concurrent_envs=4, # Allow 4 concurrent sessions
192
- )
193
- ```
194
-
195
- Then multiple clients can connect simultaneously:
196
-
197
- ```python
198
- from incident_response_env import IncidentResponseAction, IncidentResponseEnv
199
- from concurrent.futures import ThreadPoolExecutor
200
-
201
- def run_episode(client_id: int):
202
- with IncidentResponseEnv(base_url="http://localhost:8000") as env:
203
- result = env.reset()
204
- for i in range(10):
205
- result = env.step(IncidentResponseAction(message=f"Client {client_id}, step {i}"))
206
- return client_id, result.observation.message_length
207
-
208
- # Run 4 episodes concurrently
209
- with ThreadPoolExecutor(max_workers=4) as executor:
210
- results = list(executor.map(run_episode, range(4)))
211
- ```
212
-
213
- ## Development & Testing
214
-
215
- ### Direct Environment Testing
216
-
217
- Test the environment logic directly without starting the HTTP server:
218
-
219
- ```bash
220
- # From the server directory
221
- python3 server/incident_response_env_environment.py
222
- ```
223
-
224
- This verifies that:
225
- - Environment resets correctly
226
- - Step executes actions properly
227
- - State tracking works
228
- - Rewards are calculated correctly
229
-
230
- ### Running Locally
231
-
232
- Run the server locally for development:
233
-
234
- ```bash
235
- uvicorn server.app:app --reload
236
- ```
237
-
238
- ## Project Structure
239
-
240
- ```
241
- incident_response_env/
242
- โ”œโ”€โ”€ .dockerignore # Docker build exclusions
243
- โ”œโ”€โ”€ __init__.py # Module exports
244
- โ”œโ”€โ”€ README.md # This file
245
- โ”œโ”€โ”€ openenv.yaml # OpenEnv manifest
246
- โ”œโ”€โ”€ pyproject.toml # Project metadata and dependencies
247
- โ”œโ”€โ”€ uv.lock # Locked dependencies (generated)
248
- โ”œโ”€โ”€ client.py # IncidentResponseEnv client
249
- โ”œโ”€โ”€ models.py # Action and Observation models
250
- โ””โ”€โ”€ server/
251
- โ”œโ”€โ”€ __init__.py # Server module exports
252
- โ”œโ”€โ”€ incident_response_env_environment.py # Core environment logic
253
- โ”œโ”€โ”€ app.py # FastAPI application (HTTP + WebSocket endpoints)
254
- โ””โ”€โ”€ Dockerfile # Container image definition
255
- ```
 
1
+ ---
2
+ title: Incident Response Env Environment Server
3
+ emoji: ๐ŸŽฃ
4
+ colorFrom: red
5
+ colorTo: pink
6
+ sdk: docker
7
+ pinned: false
8
+ app_port: 8000
9
+ base_path: /
10
+ tags:
11
+ - openenv
12
+ ---
13
+
14
+ # Incident Response Env Environment
15
+
16
+ A simple test environment that echoes back messages. Perfect for testing the env APIs as well as demonstrating environment usage patterns.
17
+
18
+ ## Quick Start
19
+
20
+ The simplest way to use the Incident Response Env environment is through the `IncidentResponseEnv` class:
21
+
22
+ ```python
23
+ from incident_response_env import IncidentResponseAction, IncidentResponseEnv
24
+
25
+ try:
26
+ # Create environment from Docker image
27
+ incident_response_envenv = IncidentResponseEnv.from_docker_image("incident_response_env-env:latest")
28
+
29
+ # Reset
30
+ result = incident_response_envenv.reset()
31
+ print(f"Reset: {result.observation.echoed_message}")
32
+
33
+ # Send multiple messages
34
+ messages = ["Hello, World!", "Testing echo", "Final message"]
35
+
36
+ for msg in messages:
37
+ result = incident_response_envenv.step(IncidentResponseAction(message=msg))
38
+ print(f"Sent: '{msg}'")
39
+ print(f" โ†’ Echoed: '{result.observation.echoed_message}'")
40
+ print(f" โ†’ Length: {result.observation.message_length}")
41
+ print(f" โ†’ Reward: {result.reward}")
42
+
43
+ finally:
44
+ # Always clean up
45
+ incident_response_envenv.close()
46
+ ```
47
+
48
+ That's it! The `IncidentResponseEnv.from_docker_image()` method handles:
49
+ - Starting the Docker container
50
+ - Waiting for the server to be ready
51
+ - Connecting to the environment
52
+ - Container cleanup when you call `close()`
53
+
54
+ ## Building the Docker Image
55
+
56
+ Before using the environment, you need to build the Docker image:
57
+
58
+ ```bash
59
+ # From project root
60
+ docker build -t incident_response_env-env:latest -f server/Dockerfile .
61
+ ```
62
+
63
+ ## Deploying to Hugging Face Spaces
64
+
65
+ You can easily deploy your OpenEnv environment to Hugging Face Spaces using the `openenv push` command:
66
+
67
+ ```bash
68
+ # From the environment directory (where openenv.yaml is located)
69
+ openenv push
70
+
71
+ # Or specify options
72
+ openenv push --namespace my-org --private
73
+ ```
74
+
75
+ The `openenv push` command will:
76
+ 1. Validate that the directory is an OpenEnv environment (checks for `openenv.yaml`)
77
+ 2. Prepare a custom build for Hugging Face Docker space (enables web interface)
78
+ 3. Upload to Hugging Face (ensuring you're logged in)
79
+
80
+ ### Prerequisites
81
+
82
+ - Authenticate with Hugging Face: The command will prompt for login if not already authenticated
83
+
84
+ ### Options
85
+
86
+ - `--directory`, `-d`: Directory containing the OpenEnv environment (defaults to current directory)
87
+ - `--repo-id`, `-r`: Repository ID in format 'username/repo-name' (defaults to 'username/env-name' from openenv.yaml)
88
+ - `--base-image`, `-b`: Base Docker image to use (overrides Dockerfile FROM)
89
+ - `--private`: Deploy the space as private (default: public)
90
+
91
+ ### Examples
92
+
93
+ ```bash
94
+ # Push to your personal namespace (defaults to username/env-name from openenv.yaml)
95
+ openenv push
96
+
97
+ # Push to a specific repository
98
+ openenv push --repo-id my-org/my-env
99
+
100
+ # Push with a custom base image
101
+ openenv push --base-image ghcr.io/meta-pytorch/openenv-base:latest
102
+
103
+ # Push as a private space
104
+ openenv push --private
105
+
106
+ # Combine options
107
+ openenv push --repo-id my-org/my-env --base-image custom-base:latest --private
108
+ ```
109
+
110
+ After deployment, your space will be available at:
111
+ `https://huggingface.co/spaces/<repo-id>`
112
+
113
+ The deployed space includes:
114
+ - **Web Interface** at `/web` - Interactive UI for exploring the environment
115
+ - **API Documentation** at `/docs` - Full OpenAPI/Swagger interface
116
+ - **Health Check** at `/health` - Container health monitoring
117
+ - **WebSocket** at `/ws` - Persistent session endpoint for low-latency interactions
118
+
119
+ ## Environment Details
120
+
121
+ ### Action
122
+ **IncidentResponseAction**: Contains a single field
123
+ - `message` (str) - The message to echo back
124
+
125
+ ### Observation
126
+ **IncidentResponseObservation**: Contains the echo response and metadata
127
+ - `echoed_message` (str) - The message echoed back
128
+ - `message_length` (int) - Length of the message
129
+ - `reward` (float) - Reward based on message length (length ร— 0.1)
130
+ - `done` (bool) - Always False for echo environment
131
+ - `metadata` (dict) - Additional info like step count
132
+
133
+ ### Reward
134
+ The reward is calculated as: `message_length ร— 0.1`
135
+ - "Hi" โ†’ reward: 0.2
136
+ - "Hello, World!" โ†’ reward: 1.3
137
+ - Empty message โ†’ reward: 0.0
138
+
139
+ ## Advanced Usage
140
+
141
+ ### Connecting to an Existing Server
142
+
143
+ If you already have a Incident Response Env environment server running, you can connect directly:
144
+
145
+ ```python
146
+ from incident_response_env import IncidentResponseEnv
147
+
148
+ # Connect to existing server
149
+ incident_response_envenv = IncidentResponseEnv(base_url="<ENV_HTTP_URL_HERE>")
150
+
151
+ # Use as normal
152
+ result = incident_response_envenv.reset()
153
+ result = incident_response_envenv.step(IncidentResponseAction(message="Hello!"))
154
+ ```
155
+
156
+ Note: When connecting to an existing server, `incident_response_envenv.close()` will NOT stop the server.
157
+
158
+ ### Using the Context Manager
159
+
160
+ The client supports context manager usage for automatic connection management:
161
+
162
+ ```python
163
+ from incident_response_env import IncidentResponseAction, IncidentResponseEnv
164
+
165
+ # Connect with context manager (auto-connects and closes)
166
+ with IncidentResponseEnv(base_url="http://localhost:8000") as env:
167
+ result = env.reset()
168
+ print(f"Reset: {result.observation.echoed_message}")
169
+ # Multiple steps with low latency
170
+ for msg in ["Hello", "World", "!"]:
171
+ result = env.step(IncidentResponseAction(message=msg))
172
+ print(f"Echoed: {result.observation.echoed_message}")
173
+ ```
174
+
175
+ The client uses WebSocket connections for:
176
+ - **Lower latency**: No HTTP connection overhead per request
177
+ - **Persistent session**: Server maintains your environment state
178
+ - **Efficient for episodes**: Better for many sequential steps
179
+
180
+ ### Concurrent WebSocket Sessions
181
+
182
+ The server supports multiple concurrent WebSocket connections. To enable this,
183
+ modify `server/app.py` to use factory mode:
184
+
185
+ ```python
186
+ # In server/app.py - use factory mode for concurrent sessions
187
+ app = create_app(
188
+ IncidentResponseEnvironment, # Pass class, not instance
189
+ IncidentResponseAction,
190
+ IncidentResponseObservation,
191
+ max_concurrent_envs=4, # Allow 4 concurrent sessions
192
+ )
193
+ ```
194
+
195
+ Then multiple clients can connect simultaneously:
196
+
197
+ ```python
198
+ from incident_response_env import IncidentResponseAction, IncidentResponseEnv
199
+ from concurrent.futures import ThreadPoolExecutor
200
+
201
+ def run_episode(client_id: int):
202
+ with IncidentResponseEnv(base_url="http://localhost:8000") as env:
203
+ result = env.reset()
204
+ for i in range(10):
205
+ result = env.step(IncidentResponseAction(message=f"Client {client_id}, step {i}"))
206
+ return client_id, result.observation.message_length
207
+
208
+ # Run 4 episodes concurrently
209
+ with ThreadPoolExecutor(max_workers=4) as executor:
210
+ results = list(executor.map(run_episode, range(4)))
211
+ ```
212
+
213
+ ## Development & Testing
214
+
215
+ ### Direct Environment Testing
216
+
217
+ Test the environment logic directly without starting the HTTP server:
218
+
219
+ ```bash
220
+ # From the server directory
221
+ python3 server/incident_response_env_environment.py
222
+ ```
223
+
224
+ This verifies that:
225
+ - Environment resets correctly
226
+ - Step executes actions properly
227
+ - State tracking works
228
+ - Rewards are calculated correctly
229
+
230
+ ### Running Locally
231
+
232
+ Run the server locally for development:
233
+
234
+ ```bash
235
+ uvicorn server.app:app --reload
236
+ ```
237
+
238
+ ## Project Structure
239
+
240
+ ```
241
+ incident_response_env/
242
+ โ”œโ”€โ”€ .dockerignore # Docker build exclusions
243
+ โ”œโ”€โ”€ __init__.py # Module exports
244
+ โ”œโ”€โ”€ README.md # This file
245
+ โ”œโ”€โ”€ openenv.yaml # OpenEnv manifest
246
+ โ”œโ”€โ”€ pyproject.toml # Project metadata and dependencies
247
+ โ”œโ”€โ”€ uv.lock # Locked dependencies (generated)
248
+ โ”œโ”€โ”€ client.py # IncidentResponseEnv client
249
+ โ”œโ”€โ”€ models.py # Action and Observation models
250
+ โ””โ”€โ”€ server/
251
+ โ”œโ”€โ”€ __init__.py # Server module exports
252
+ โ”œโ”€โ”€ incident_response_env_environment.py # Core environment logic
253
+ โ”œโ”€โ”€ app.py # FastAPI application (HTTP + WebSocket endpoints)
254
+ โ””โ”€โ”€ Dockerfile # Container image definition
255
+ ```
server/_notebook.html CHANGED
@@ -9001,9 +9001,9 @@ Unsloth: Fast downloading is enabled - ignore downloading bars which are red col
9001
  </div>
9002
  <div class="jp-OutputArea-child">
9003
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
9004
- <div class="jupyter-widgets jp-OutputArea-output" id="5a82074c-8f9c-45ea-bf9d-7a40d8dfdf0a" tabindex="0">
9005
  <script type="text/javascript">
9006
- var element = document.getElementById('5a82074c-8f9c-45ea-bf9d-7a40d8dfdf0a');
9007
  </script>
9008
  <script type="application/vnd.jupyter.widget-view+json">
9009
  {"model_id": "a43b656123724da68f8ce0b1d3c8ce6f", "version_major": 2, "version_minor": 0}
@@ -9012,9 +9012,9 @@ var element = document.getElementById('5a82074c-8f9c-45ea-bf9d-7a40d8dfdf0a');
9012
  </div>
9013
  <div class="jp-OutputArea-child">
9014
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
9015
- <div class="jupyter-widgets jp-OutputArea-output" id="44c9921c-4aaf-4dff-a61d-e71efefd088c" tabindex="0">
9016
  <script type="text/javascript">
9017
- var element = document.getElementById('44c9921c-4aaf-4dff-a61d-e71efefd088c');
9018
  </script>
9019
  <script type="application/vnd.jupyter.widget-view+json">
9020
  {"model_id": "55dfc654c378407799fea2077dd9a102", "version_major": 2, "version_minor": 0}
@@ -9023,9 +9023,9 @@ var element = document.getElementById('44c9921c-4aaf-4dff-a61d-e71efefd088c');
9023
  </div>
9024
  <div class="jp-OutputArea-child">
9025
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
9026
- <div class="jupyter-widgets jp-OutputArea-output" id="d17747f9-b240-43ef-bd9b-d10a239534a7" tabindex="0">
9027
  <script type="text/javascript">
9028
- var element = document.getElementById('d17747f9-b240-43ef-bd9b-d10a239534a7');
9029
  </script>
9030
  <script type="application/vnd.jupyter.widget-view+json">
9031
  {"model_id": "e0b7e6ef22854ded9e3b5334ae2314d7", "version_major": 2, "version_minor": 0}
@@ -9034,9 +9034,9 @@ var element = document.getElementById('d17747f9-b240-43ef-bd9b-d10a239534a7');
9034
  </div>
9035
  <div class="jp-OutputArea-child">
9036
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
9037
- <div class="jupyter-widgets jp-OutputArea-output" id="3dd2bf99-e8db-4daf-85c9-81fb8fa46df2" tabindex="0">
9038
  <script type="text/javascript">
9039
- var element = document.getElementById('3dd2bf99-e8db-4daf-85c9-81fb8fa46df2');
9040
  </script>
9041
  <script type="application/vnd.jupyter.widget-view+json">
9042
  {"model_id": "d4406d23036646ba87287c7a34db6967", "version_major": 2, "version_minor": 0}
@@ -9045,9 +9045,9 @@ var element = document.getElementById('3dd2bf99-e8db-4daf-85c9-81fb8fa46df2');
9045
  </div>
9046
  <div class="jp-OutputArea-child">
9047
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
9048
- <div class="jupyter-widgets jp-OutputArea-output" id="3a32585f-caec-4584-b57c-0d4457510095" tabindex="0">
9049
  <script type="text/javascript">
9050
- var element = document.getElementById('3a32585f-caec-4584-b57c-0d4457510095');
9051
  </script>
9052
  <script type="application/vnd.jupyter.widget-view+json">
9053
  {"model_id": "7f5e0fa0d7f04f73a1c71206b2d14d03", "version_major": 2, "version_minor": 0}
@@ -9056,9 +9056,9 @@ var element = document.getElementById('3a32585f-caec-4584-b57c-0d4457510095');
9056
  </div>
9057
  <div class="jp-OutputArea-child">
9058
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
9059
- <div class="jupyter-widgets jp-OutputArea-output" id="f25275b2-2a77-4cad-a72b-52811f6d2bab" tabindex="0">
9060
  <script type="text/javascript">
9061
- var element = document.getElementById('f25275b2-2a77-4cad-a72b-52811f6d2bab');
9062
  </script>
9063
  <script type="application/vnd.jupyter.widget-view+json">
9064
  {"model_id": "d59108a79279418b89082c3c35c791b5", "version_major": 2, "version_minor": 0}
@@ -9067,9 +9067,9 @@ var element = document.getElementById('f25275b2-2a77-4cad-a72b-52811f6d2bab');
9067
  </div>
9068
  <div class="jp-OutputArea-child">
9069
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
9070
- <div class="jupyter-widgets jp-OutputArea-output" id="78585ee7-2cc7-4cea-b4cc-921123492381" tabindex="0">
9071
  <script type="text/javascript">
9072
- var element = document.getElementById('78585ee7-2cc7-4cea-b4cc-921123492381');
9073
  </script>
9074
  <script type="application/vnd.jupyter.widget-view+json">
9075
  {"model_id": "30b37f3a86b54844b1b522f63f94254e", "version_major": 2, "version_minor": 0}
@@ -9078,9 +9078,9 @@ var element = document.getElementById('78585ee7-2cc7-4cea-b4cc-921123492381');
9078
  </div>
9079
  <div class="jp-OutputArea-child">
9080
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
9081
- <div class="jupyter-widgets jp-OutputArea-output" id="d83fb147-f81d-4f90-85f5-11e0de35a95f" tabindex="0">
9082
  <script type="text/javascript">
9083
- var element = document.getElementById('d83fb147-f81d-4f90-85f5-11e0de35a95f');
9084
  </script>
9085
  <script type="application/vnd.jupyter.widget-view+json">
9086
  {"model_id": "01e641d29b9d41da93d7cc9c693abdcd", "version_major": 2, "version_minor": 0}
@@ -22886,9 +22886,9 @@ Unsloth: QLoRA and full finetuning all not selected. Switching to 16bit LoRA.
22886
  </div>
22887
  <div class="jp-OutputArea-child">
22888
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
22889
- <div class="jupyter-widgets jp-OutputArea-output" id="3c098df9-c846-4621-a8f5-f74b8267fba7" tabindex="0">
22890
  <script type="text/javascript">
22891
- var element = document.getElementById('3c098df9-c846-4621-a8f5-f74b8267fba7');
22892
  </script>
22893
  <script type="application/vnd.jupyter.widget-view+json">
22894
  {"model_id": "bceb1ef5489f43c18eb7b846041dc50a", "version_major": 2, "version_minor": 0}
@@ -22897,9 +22897,9 @@ var element = document.getElementById('3c098df9-c846-4621-a8f5-f74b8267fba7');
22897
  </div>
22898
  <div class="jp-OutputArea-child">
22899
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
22900
- <div class="jupyter-widgets jp-OutputArea-output" id="4269952f-96aa-4518-8633-78b2c00ac1f6" tabindex="0">
22901
  <script type="text/javascript">
22902
- var element = document.getElementById('4269952f-96aa-4518-8633-78b2c00ac1f6');
22903
  </script>
22904
  <script type="application/vnd.jupyter.widget-view+json">
22905
  {"model_id": "1473bf43ffc248e48fdad86849869776", "version_major": 2, "version_minor": 0}
@@ -22908,9 +22908,9 @@ var element = document.getElementById('4269952f-96aa-4518-8633-78b2c00ac1f6');
22908
  </div>
22909
  <div class="jp-OutputArea-child">
22910
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
22911
- <div class="jupyter-widgets jp-OutputArea-output" id="34e6c9f1-2b6e-4484-85c5-7771e5e841fc" tabindex="0">
22912
  <script type="text/javascript">
22913
- var element = document.getElementById('34e6c9f1-2b6e-4484-85c5-7771e5e841fc');
22914
  </script>
22915
  <script type="application/vnd.jupyter.widget-view+json">
22916
  {"model_id": "2d1950fd251549968618281e1717672c", "version_major": 2, "version_minor": 0}
@@ -22919,9 +22919,9 @@ var element = document.getElementById('34e6c9f1-2b6e-4484-85c5-7771e5e841fc');
22919
  </div>
22920
  <div class="jp-OutputArea-child">
22921
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
22922
- <div class="jupyter-widgets jp-OutputArea-output" id="0d0d5df8-4c18-4d89-ae02-649a01945aa5" tabindex="0">
22923
  <script type="text/javascript">
22924
- var element = document.getElementById('0d0d5df8-4c18-4d89-ae02-649a01945aa5');
22925
  </script>
22926
  <script type="application/vnd.jupyter.widget-view+json">
22927
  {"model_id": "d37ad2830cd44a94bc54195b03d794c8", "version_major": 2, "version_minor": 0}
@@ -22930,9 +22930,9 @@ var element = document.getElementById('0d0d5df8-4c18-4d89-ae02-649a01945aa5');
22930
  </div>
22931
  <div class="jp-OutputArea-child">
22932
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
22933
- <div class="jupyter-widgets jp-OutputArea-output" id="a3c7f50f-a609-44de-acea-2e9d5f3b60ae" tabindex="0">
22934
  <script type="text/javascript">
22935
- var element = document.getElementById('a3c7f50f-a609-44de-acea-2e9d5f3b60ae');
22936
  </script>
22937
  <script type="application/vnd.jupyter.widget-view+json">
22938
  {"model_id": "cc1680f141514c1d86237fd46ed6ab50", "version_major": 2, "version_minor": 0}
@@ -22941,9 +22941,9 @@ var element = document.getElementById('a3c7f50f-a609-44de-acea-2e9d5f3b60ae');
22941
  </div>
22942
  <div class="jp-OutputArea-child">
22943
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
22944
- <div class="jupyter-widgets jp-OutputArea-output" id="0a47160a-f191-47e0-be62-b7f65e10bb2f" tabindex="0">
22945
  <script type="text/javascript">
22946
- var element = document.getElementById('0a47160a-f191-47e0-be62-b7f65e10bb2f');
22947
  </script>
22948
  <script type="application/vnd.jupyter.widget-view+json">
22949
  {"model_id": "fa1de6e7572442a18a37ecdd236be73c", "version_major": 2, "version_minor": 0}
@@ -22952,9 +22952,9 @@ var element = document.getElementById('0a47160a-f191-47e0-be62-b7f65e10bb2f');
22952
  </div>
22953
  <div class="jp-OutputArea-child">
22954
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
22955
- <div class="jupyter-widgets jp-OutputArea-output" id="f2ac9dee-fb3b-4d05-b8bb-92872650f76d" tabindex="0">
22956
  <script type="text/javascript">
22957
- var element = document.getElementById('f2ac9dee-fb3b-4d05-b8bb-92872650f76d');
22958
  </script>
22959
  <script type="application/vnd.jupyter.widget-view+json">
22960
  {"model_id": "fcc3dfc78fba44fea8fd65a09b32b39f", "version_major": 2, "version_minor": 0}
@@ -22963,9 +22963,9 @@ var element = document.getElementById('f2ac9dee-fb3b-4d05-b8bb-92872650f76d');
22963
  </div>
22964
  <div class="jp-OutputArea-child">
22965
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
22966
- <div class="jupyter-widgets jp-OutputArea-output" id="0428d5a7-8dcb-41c5-b16a-a68ba7535232" tabindex="0">
22967
  <script type="text/javascript">
22968
- var element = document.getElementById('0428d5a7-8dcb-41c5-b16a-a68ba7535232');
22969
  </script>
22970
  <script type="application/vnd.jupyter.widget-view+json">
22971
  {"model_id": "2b44dac6f6ed48269f2ba327095e30a7", "version_major": 2, "version_minor": 0}
@@ -23381,9 +23381,9 @@ Total notebook runtime: 133.89 minutes (8033.2 seconds)
23381
  <div class="jp-OutputArea jp-Cell-outputArea">
23382
  <div class="jp-OutputArea-child">
23383
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
23384
- <div class="jupyter-widgets jp-OutputArea-output" id="f73d028d-3d4a-4e5a-abc9-62ac4c1a2ffe" tabindex="0">
23385
  <script type="text/javascript">
23386
- var element = document.getElementById('f73d028d-3d4a-4e5a-abc9-62ac4c1a2ffe');
23387
  </script>
23388
  <script type="application/vnd.jupyter.widget-view+json">
23389
  {"model_id": "227594b06c7744f28319d7337a32cb5e", "version_major": 2, "version_minor": 0}
@@ -23392,9 +23392,9 @@ var element = document.getElementById('f73d028d-3d4a-4e5a-abc9-62ac4c1a2ffe');
23392
  </div>
23393
  <div class="jp-OutputArea-child">
23394
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
23395
- <div class="jupyter-widgets jp-OutputArea-output" id="9f00317b-9a07-4c3d-869a-2179157b31a2" tabindex="0">
23396
  <script type="text/javascript">
23397
- var element = document.getElementById('9f00317b-9a07-4c3d-869a-2179157b31a2');
23398
  </script>
23399
  <script type="application/vnd.jupyter.widget-view+json">
23400
  {"model_id": "85bd070deb1f4c3f9a76db4f0269a119", "version_major": 2, "version_minor": 0}
@@ -23439,9 +23439,9 @@ var element = document.getElementById('9f00317b-9a07-4c3d-869a-2179157b31a2');
23439
  <div class="jp-OutputArea jp-Cell-outputArea">
23440
  <div class="jp-OutputArea-child">
23441
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
23442
- <div class="jupyter-widgets jp-OutputArea-output" id="0829c2c9-1a21-431d-9ad4-04d76445ad33" tabindex="0">
23443
  <script type="text/javascript">
23444
- var element = document.getElementById('0829c2c9-1a21-431d-9ad4-04d76445ad33');
23445
  </script>
23446
  <script type="application/vnd.jupyter.widget-view+json">
23447
  {"model_id": "8b799481419c44f583f316a3eaed4e1c", "version_major": 2, "version_minor": 0}
@@ -23450,9 +23450,9 @@ var element = document.getElementById('0829c2c9-1a21-431d-9ad4-04d76445ad33');
23450
  </div>
23451
  <div class="jp-OutputArea-child">
23452
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
23453
- <div class="jupyter-widgets jp-OutputArea-output" id="d827d3c6-8d95-4b69-9e67-0485301a8ebe" tabindex="0">
23454
  <script type="text/javascript">
23455
- var element = document.getElementById('d827d3c6-8d95-4b69-9e67-0485301a8ebe');
23456
  </script>
23457
  <script type="application/vnd.jupyter.widget-view+json">
23458
  {"model_id": "c84bd0c0cd6d4e3780221bc8497b4359", "version_major": 2, "version_minor": 0}
 
9001
  </div>
9002
  <div class="jp-OutputArea-child">
9003
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
9004
+ <div class="jupyter-widgets jp-OutputArea-output" id="4665c438-18da-42d7-b933-ffd6eaadd2a6" tabindex="0">
9005
  <script type="text/javascript">
9006
+ var element = document.getElementById('4665c438-18da-42d7-b933-ffd6eaadd2a6');
9007
  </script>
9008
  <script type="application/vnd.jupyter.widget-view+json">
9009
  {"model_id": "a43b656123724da68f8ce0b1d3c8ce6f", "version_major": 2, "version_minor": 0}
 
9012
  </div>
9013
  <div class="jp-OutputArea-child">
9014
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
9015
+ <div class="jupyter-widgets jp-OutputArea-output" id="724f32b1-4f56-40cc-9650-581bbc9bbc4f" tabindex="0">
9016
  <script type="text/javascript">
9017
+ var element = document.getElementById('724f32b1-4f56-40cc-9650-581bbc9bbc4f');
9018
  </script>
9019
  <script type="application/vnd.jupyter.widget-view+json">
9020
  {"model_id": "55dfc654c378407799fea2077dd9a102", "version_major": 2, "version_minor": 0}
 
9023
  </div>
9024
  <div class="jp-OutputArea-child">
9025
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
9026
+ <div class="jupyter-widgets jp-OutputArea-output" id="cd3368e7-baaf-4ad7-a0f7-7bf3935a38fa" tabindex="0">
9027
  <script type="text/javascript">
9028
+ var element = document.getElementById('cd3368e7-baaf-4ad7-a0f7-7bf3935a38fa');
9029
  </script>
9030
  <script type="application/vnd.jupyter.widget-view+json">
9031
  {"model_id": "e0b7e6ef22854ded9e3b5334ae2314d7", "version_major": 2, "version_minor": 0}
 
9034
  </div>
9035
  <div class="jp-OutputArea-child">
9036
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
9037
+ <div class="jupyter-widgets jp-OutputArea-output" id="93e859d4-7268-41c4-b1a6-bf227811076e" tabindex="0">
9038
  <script type="text/javascript">
9039
+ var element = document.getElementById('93e859d4-7268-41c4-b1a6-bf227811076e');
9040
  </script>
9041
  <script type="application/vnd.jupyter.widget-view+json">
9042
  {"model_id": "d4406d23036646ba87287c7a34db6967", "version_major": 2, "version_minor": 0}
 
9045
  </div>
9046
  <div class="jp-OutputArea-child">
9047
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
9048
+ <div class="jupyter-widgets jp-OutputArea-output" id="aa50e38d-d271-40e8-a1f0-74c8717a1af1" tabindex="0">
9049
  <script type="text/javascript">
9050
+ var element = document.getElementById('aa50e38d-d271-40e8-a1f0-74c8717a1af1');
9051
  </script>
9052
  <script type="application/vnd.jupyter.widget-view+json">
9053
  {"model_id": "7f5e0fa0d7f04f73a1c71206b2d14d03", "version_major": 2, "version_minor": 0}
 
9056
  </div>
9057
  <div class="jp-OutputArea-child">
9058
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
9059
+ <div class="jupyter-widgets jp-OutputArea-output" id="bcc3300f-6eda-4e37-bf3e-2cb2568264ca" tabindex="0">
9060
  <script type="text/javascript">
9061
+ var element = document.getElementById('bcc3300f-6eda-4e37-bf3e-2cb2568264ca');
9062
  </script>
9063
  <script type="application/vnd.jupyter.widget-view+json">
9064
  {"model_id": "d59108a79279418b89082c3c35c791b5", "version_major": 2, "version_minor": 0}
 
9067
  </div>
9068
  <div class="jp-OutputArea-child">
9069
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
9070
+ <div class="jupyter-widgets jp-OutputArea-output" id="bf615dad-1d33-41f8-87d8-9eb5f2f6b0d1" tabindex="0">
9071
  <script type="text/javascript">
9072
+ var element = document.getElementById('bf615dad-1d33-41f8-87d8-9eb5f2f6b0d1');
9073
  </script>
9074
  <script type="application/vnd.jupyter.widget-view+json">
9075
  {"model_id": "30b37f3a86b54844b1b522f63f94254e", "version_major": 2, "version_minor": 0}
 
9078
  </div>
9079
  <div class="jp-OutputArea-child">
9080
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
9081
+ <div class="jupyter-widgets jp-OutputArea-output" id="070b76e1-d222-4ab8-ab76-c199b158522c" tabindex="0">
9082
  <script type="text/javascript">
9083
+ var element = document.getElementById('070b76e1-d222-4ab8-ab76-c199b158522c');
9084
  </script>
9085
  <script type="application/vnd.jupyter.widget-view+json">
9086
  {"model_id": "01e641d29b9d41da93d7cc9c693abdcd", "version_major": 2, "version_minor": 0}
 
22886
  </div>
22887
  <div class="jp-OutputArea-child">
22888
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
22889
+ <div class="jupyter-widgets jp-OutputArea-output" id="974a857b-d684-46ed-96e9-8d0bb38ae70b" tabindex="0">
22890
  <script type="text/javascript">
22891
+ var element = document.getElementById('974a857b-d684-46ed-96e9-8d0bb38ae70b');
22892
  </script>
22893
  <script type="application/vnd.jupyter.widget-view+json">
22894
  {"model_id": "bceb1ef5489f43c18eb7b846041dc50a", "version_major": 2, "version_minor": 0}
 
22897
  </div>
22898
  <div class="jp-OutputArea-child">
22899
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
22900
+ <div class="jupyter-widgets jp-OutputArea-output" id="38f2742e-e460-4e84-82d3-5485919d4793" tabindex="0">
22901
  <script type="text/javascript">
22902
+ var element = document.getElementById('38f2742e-e460-4e84-82d3-5485919d4793');
22903
  </script>
22904
  <script type="application/vnd.jupyter.widget-view+json">
22905
  {"model_id": "1473bf43ffc248e48fdad86849869776", "version_major": 2, "version_minor": 0}
 
22908
  </div>
22909
  <div class="jp-OutputArea-child">
22910
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
22911
+ <div class="jupyter-widgets jp-OutputArea-output" id="015d43d4-9066-40d4-b556-f819e055c2cd" tabindex="0">
22912
  <script type="text/javascript">
22913
+ var element = document.getElementById('015d43d4-9066-40d4-b556-f819e055c2cd');
22914
  </script>
22915
  <script type="application/vnd.jupyter.widget-view+json">
22916
  {"model_id": "2d1950fd251549968618281e1717672c", "version_major": 2, "version_minor": 0}
 
22919
  </div>
22920
  <div class="jp-OutputArea-child">
22921
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
22922
+ <div class="jupyter-widgets jp-OutputArea-output" id="1d5857a3-53df-4584-8197-685d1b35a66b" tabindex="0">
22923
  <script type="text/javascript">
22924
+ var element = document.getElementById('1d5857a3-53df-4584-8197-685d1b35a66b');
22925
  </script>
22926
  <script type="application/vnd.jupyter.widget-view+json">
22927
  {"model_id": "d37ad2830cd44a94bc54195b03d794c8", "version_major": 2, "version_minor": 0}
 
22930
  </div>
22931
  <div class="jp-OutputArea-child">
22932
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
22933
+ <div class="jupyter-widgets jp-OutputArea-output" id="0b2ee197-0fc0-4092-a292-d43ceb3baaeb" tabindex="0">
22934
  <script type="text/javascript">
22935
+ var element = document.getElementById('0b2ee197-0fc0-4092-a292-d43ceb3baaeb');
22936
  </script>
22937
  <script type="application/vnd.jupyter.widget-view+json">
22938
  {"model_id": "cc1680f141514c1d86237fd46ed6ab50", "version_major": 2, "version_minor": 0}
 
22941
  </div>
22942
  <div class="jp-OutputArea-child">
22943
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
22944
+ <div class="jupyter-widgets jp-OutputArea-output" id="f70afaaf-4522-47b7-9ce8-9a83244cb046" tabindex="0">
22945
  <script type="text/javascript">
22946
+ var element = document.getElementById('f70afaaf-4522-47b7-9ce8-9a83244cb046');
22947
  </script>
22948
  <script type="application/vnd.jupyter.widget-view+json">
22949
  {"model_id": "fa1de6e7572442a18a37ecdd236be73c", "version_major": 2, "version_minor": 0}
 
22952
  </div>
22953
  <div class="jp-OutputArea-child">
22954
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
22955
+ <div class="jupyter-widgets jp-OutputArea-output" id="8e6d6c37-f9cb-4429-ba45-d32f9f6c6cc5" tabindex="0">
22956
  <script type="text/javascript">
22957
+ var element = document.getElementById('8e6d6c37-f9cb-4429-ba45-d32f9f6c6cc5');
22958
  </script>
22959
  <script type="application/vnd.jupyter.widget-view+json">
22960
  {"model_id": "fcc3dfc78fba44fea8fd65a09b32b39f", "version_major": 2, "version_minor": 0}
 
22963
  </div>
22964
  <div class="jp-OutputArea-child">
22965
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
22966
+ <div class="jupyter-widgets jp-OutputArea-output" id="4eb97556-5063-473b-b1fe-b961897e3ba4" tabindex="0">
22967
  <script type="text/javascript">
22968
+ var element = document.getElementById('4eb97556-5063-473b-b1fe-b961897e3ba4');
22969
  </script>
22970
  <script type="application/vnd.jupyter.widget-view+json">
22971
  {"model_id": "2b44dac6f6ed48269f2ba327095e30a7", "version_major": 2, "version_minor": 0}
 
23381
  <div class="jp-OutputArea jp-Cell-outputArea">
23382
  <div class="jp-OutputArea-child">
23383
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
23384
+ <div class="jupyter-widgets jp-OutputArea-output" id="bf5ca71a-1ddc-4478-a83c-f44fa34eacfb" tabindex="0">
23385
  <script type="text/javascript">
23386
+ var element = document.getElementById('bf5ca71a-1ddc-4478-a83c-f44fa34eacfb');
23387
  </script>
23388
  <script type="application/vnd.jupyter.widget-view+json">
23389
  {"model_id": "227594b06c7744f28319d7337a32cb5e", "version_major": 2, "version_minor": 0}
 
23392
  </div>
23393
  <div class="jp-OutputArea-child">
23394
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
23395
+ <div class="jupyter-widgets jp-OutputArea-output" id="a787076d-47b9-43c0-acaf-67ead9a7f964" tabindex="0">
23396
  <script type="text/javascript">
23397
+ var element = document.getElementById('a787076d-47b9-43c0-acaf-67ead9a7f964');
23398
  </script>
23399
  <script type="application/vnd.jupyter.widget-view+json">
23400
  {"model_id": "85bd070deb1f4c3f9a76db4f0269a119", "version_major": 2, "version_minor": 0}
 
23439
  <div class="jp-OutputArea jp-Cell-outputArea">
23440
  <div class="jp-OutputArea-child">
23441
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
23442
+ <div class="jupyter-widgets jp-OutputArea-output" id="a59e8d06-5446-4a8b-a07c-6ec7364ae143" tabindex="0">
23443
  <script type="text/javascript">
23444
+ var element = document.getElementById('a59e8d06-5446-4a8b-a07c-6ec7364ae143');
23445
  </script>
23446
  <script type="application/vnd.jupyter.widget-view+json">
23447
  {"model_id": "8b799481419c44f583f316a3eaed4e1c", "version_major": 2, "version_minor": 0}
 
23450
  </div>
23451
  <div class="jp-OutputArea-child">
23452
  <div class="jp-OutputPrompt jp-OutputArea-prompt"></div>
23453
+ <div class="jupyter-widgets jp-OutputArea-output" id="04de4636-abae-4ee9-95a7-df1c50955830" tabindex="0">
23454
  <script type="text/javascript">
23455
+ var element = document.getElementById('04de4636-abae-4ee9-95a7-df1c50955830');
23456
  </script>
23457
  <script type="application/vnd.jupyter.widget-view+json">
23458
  {"model_id": "c84bd0c0cd6d4e3780221bc8497b4359", "version_major": 2, "version_minor": 0}
server/custom_ui.py CHANGED
@@ -277,39 +277,28 @@ def build_custom_ui(
277
 
278
  async def run_model(role: str):
279
  """
280
- Load the GRPO checkpoint for the current role and auto-generate an action.
281
- Streams status updates (model loading, inference) via gr.update on the status box.
282
- After inference, automatically calls step_env with the generated action.
283
  """
 
 
284
  if not _MODEL_AVAILABLE:
285
- yield (gr.update(), gr.update(), gr.update(), "", "", "transformers not installed โ€” cannot run model.", gr.update())
286
- return
287
 
288
  if role not in ROLE_CHOICES:
289
- yield (gr.update(), gr.update(), gr.update(), "", "", "Select a role first.", gr.update())
290
- return
291
-
292
- status_msgs: List[str] = []
293
-
294
- def _status_cb(msg: str):
295
- status_msgs.append(msg)
296
 
297
- loop = asyncio.get_event_loop()
298
- # We need the prompt from the current observation to pass to the model.
299
- # Retrieve it via the web_manager's current observation.
300
  try:
301
- current_obs = None
302
- if (
303
- web_manager.episode_state
304
- and web_manager.episode_state.current_observation
305
- ):
306
- current_obs = web_manager.episode_state.current_observation
307
  except Exception:
308
  current_obs = None
309
 
310
  if current_obs is None:
311
- # Auto-reset first
312
- yield (gr.update(), gr.update(), gr.update(), "", "", "Auto-resetting episodeโ€ฆ", gr.update())
313
  await web_manager.reset_environment()
314
  try:
315
  current_obs = web_manager.episode_state.current_observation
@@ -319,49 +308,42 @@ def build_custom_ui(
319
  prompt = (current_obs or {}).get("prompt", "") if isinstance(current_obs, dict) else ""
320
 
321
  if not prompt:
322
- yield (gr.update(), gr.update(), gr.update(), "", "", "No prompt available โ€” click Reset first.", gr.update())
323
- return
324
 
325
- yield (gr.update(), gr.update(), gr.update(), "", "", f"Loading {role} model from HuggingFaceโ€ฆ", gr.update())
326
 
 
 
 
 
327
  try:
328
  command = await loop.run_in_executor(
329
  _MODEL_EXECUTOR,
330
  lambda: _model_generate_action(role, prompt, _status_cb),
331
  )
332
  except Exception as exc:
333
- yield (gr.update(), gr.update(), gr.update(), "", "", f"Model error: {exc}", gr.update())
334
- return
335
 
336
  all_status = " โ†’ ".join(status_msgs)
337
 
338
  if not command:
339
- yield (gr.update(), gr.update(), gr.update(), "", "", f"Model produced invalid output. {all_status}", gr.update())
340
- return
341
 
342
- # Map model command back to an action_id for display consistency
343
- universe = ATTACKS if role == "attacker" else DEFENSES
344
- prefix = "ATTACK" if role == "attacker" else "DEFEND"
345
- action_name = command.replace(f"{prefix}:", "").strip()
346
- ai = universe.index(action_name) if action_name in universe else 0
347
-
348
- # Step the environment with the generated command
349
  try:
350
  data = await web_manager.step_environment({"command": command, "role": role})
351
  except Exception as exc:
352
- yield (gr.update(), gr.update(), gr.update(), "", "", f"Step error: {exc}", gr.update())
353
- return
354
 
355
- obs = data.get("observation", {}) or {}
356
- next_role = obs.get("next_role", role)
357
- health = obs.get("health", 1.0)
358
  status_text = obs.get("status", "STABLE")
359
- done = bool(obs.get("done", False))
360
  role_value = next_role if next_role in ROLE_CHOICES else role
361
  new_choices = _action_choices(role_value)
362
  done_note = " Episode complete โ€” click Reset." if done else ""
363
 
364
- yield (
365
  gr.update(value=role_value),
366
  gr.update(choices=new_choices, value=new_choices[0][1] if new_choices else None,
367
  label=f"Action ID (0..{len(new_choices) - 1})"),
@@ -369,7 +351,6 @@ def build_custom_ui(
369
  "",
370
  json.dumps(_strip_prompt_from_response_for_display(data), indent=2),
371
  f"Model โ†’ {command} | health={health:.0%} | {status_text} | next={next_role}.{done_note}",
372
- gr.update(value=ai),
373
  )
374
 
375
  initial_role = "attacker"
@@ -382,38 +363,44 @@ def build_custom_ui(
382
  <style>
383
  .oe_sidebar {
384
  position: fixed;
385
- left: 16px;
386
- top: 16px;
387
- width: 220px;
388
  z-index: 9999;
389
- background: rgba(15, 23, 42, 0.92);
390
- border: 1px solid rgba(148, 163, 184, 0.25);
391
  border-radius: 12px;
392
- padding: 12px;
393
- backdrop-filter: blur(8px);
 
 
 
 
 
 
 
 
394
  }
395
- .oe_sidebar_title { font-weight: 700; margin: 0 0 8px 0; font-size: 13px; color: #e2e8f0; }
396
  .oe_sidebar a {
397
  display: block;
398
- padding: 8px 10px;
399
  margin: 6px 0;
400
- border-radius: 10px;
401
- color: #e2e8f0;
402
  text-decoration: none;
403
  font-size: 13px;
404
- border: 1px solid rgba(148, 163, 184, 0.18);
405
- background: rgba(30, 41, 59, 0.55);
406
- }
407
- .oe_sidebar a:hover { background: rgba(51, 65, 85, 0.75); }
408
- /* Add a little breathing room so sidebar doesn't cover content on narrow screens. */
409
- @media (min-width: 1100px) {
410
- .col-left { margin-left: 240px; }
411
  }
 
 
412
  </style>
413
  <nav class="oe_sidebar">
414
  <div class="oe_sidebar_title">Jump to</div>
415
- <a href="#interactive_playground">Interactive playground</a>
416
- <a href="#grpo_training_notebook">GRPO training notebook</a>
417
  </nav>
418
  """
419
  )
@@ -487,7 +474,7 @@ def build_custom_ui(
487
  model_btn.click(
488
  fn=run_model,
489
  inputs=[role_dd],
490
- outputs=[role_dd, action_dd, mapping_md, obs_md, raw_json, status_box, action_dd],
491
  )
492
  state_btn.click(fn=get_state_sync, outputs=[raw_json])
493
 
 
277
 
278
  async def run_model(role: str):
279
  """
280
+ Load the GRPO checkpoint for the current role and auto-generate an action,
281
+ then step the environment with the result.
 
282
  """
283
+ _blank = (gr.update(), gr.update(), gr.update(), "", "", "")
284
+
285
  if not _MODEL_AVAILABLE:
286
+ return (*_blank[:5], "transformers/torch not installed โ€” cannot run model.")
 
287
 
288
  if role not in ROLE_CHOICES:
289
+ return (*_blank[:5], "Select a role first.")
 
 
 
 
 
 
290
 
291
+ # Ensure an episode is active
 
 
292
  try:
293
+ current_obs = (
294
+ web_manager.episode_state.current_observation
295
+ if web_manager.episode_state and web_manager.episode_state.current_observation
296
+ else None
297
+ )
 
298
  except Exception:
299
  current_obs = None
300
 
301
  if current_obs is None:
 
 
302
  await web_manager.reset_environment()
303
  try:
304
  current_obs = web_manager.episode_state.current_observation
 
308
  prompt = (current_obs or {}).get("prompt", "") if isinstance(current_obs, dict) else ""
309
 
310
  if not prompt:
311
+ return (*_blank[:5], "No prompt yet โ€” click Reset first.")
 
312
 
313
+ status_msgs: List[str] = []
314
 
315
+ def _status_cb(msg: str):
316
+ status_msgs.append(msg)
317
+
318
+ loop = asyncio.get_event_loop()
319
  try:
320
  command = await loop.run_in_executor(
321
  _MODEL_EXECUTOR,
322
  lambda: _model_generate_action(role, prompt, _status_cb),
323
  )
324
  except Exception as exc:
325
+ return (*_blank[:5], f"Model error: {exc}")
 
326
 
327
  all_status = " โ†’ ".join(status_msgs)
328
 
329
  if not command:
330
+ return (*_blank[:5], f"Model produced no valid action. {all_status}")
 
331
 
 
 
 
 
 
 
 
332
  try:
333
  data = await web_manager.step_environment({"command": command, "role": role})
334
  except Exception as exc:
335
+ return (*_blank[:5], f"Step error after model action: {exc}")
 
336
 
337
+ obs = data.get("observation", {}) or {}
338
+ next_role = obs.get("next_role", role)
339
+ health = obs.get("health", 1.0)
340
  status_text = obs.get("status", "STABLE")
341
+ done = bool(obs.get("done", False))
342
  role_value = next_role if next_role in ROLE_CHOICES else role
343
  new_choices = _action_choices(role_value)
344
  done_note = " Episode complete โ€” click Reset." if done else ""
345
 
346
+ return (
347
  gr.update(value=role_value),
348
  gr.update(choices=new_choices, value=new_choices[0][1] if new_choices else None,
349
  label=f"Action ID (0..{len(new_choices) - 1})"),
 
351
  "",
352
  json.dumps(_strip_prompt_from_response_for_display(data), indent=2),
353
  f"Model โ†’ {command} | health={health:.0%} | {status_text} | next={next_role}.{done_note}",
 
354
  )
355
 
356
  initial_role = "attacker"
 
363
  <style>
364
  .oe_sidebar {
365
  position: fixed;
366
+ left: 14px;
367
+ top: 14px;
368
+ width: 210px;
369
  z-index: 9999;
370
+ background: #1e293b;
371
+ border: 1px solid #475569;
372
  border-radius: 12px;
373
+ padding: 14px 12px;
374
+ box-shadow: 0 4px 24px rgba(0,0,0,0.5);
375
+ }
376
+ .oe_sidebar_title {
377
+ font-weight: 700;
378
+ margin: 0 0 10px 0;
379
+ font-size: 12px;
380
+ letter-spacing: .08em;
381
+ text-transform: uppercase;
382
+ color: #94a3b8;
383
  }
 
384
  .oe_sidebar a {
385
  display: block;
386
+ padding: 9px 12px;
387
  margin: 6px 0;
388
+ border-radius: 8px;
389
+ color: #f1f5f9;
390
  text-decoration: none;
391
  font-size: 13px;
392
+ font-weight: 500;
393
+ background: #334155;
394
+ border: 1px solid #475569;
395
+ transition: background .15s;
 
 
 
396
  }
397
+ .oe_sidebar a:hover { background: #4a6080; color: #ffffff; }
398
+ @media (min-width: 1100px) { .col-left { margin-left: 240px; } }
399
  </style>
400
  <nav class="oe_sidebar">
401
  <div class="oe_sidebar_title">Jump to</div>
402
+ <a href="#interactive_playground">๐ŸŽฎ Interactive Playground</a>
403
+ <a href="#grpo_training_notebook">๐Ÿ““ GRPO Training Notebook</a>
404
  </nav>
405
  """
406
  )
 
474
  model_btn.click(
475
  fn=run_model,
476
  inputs=[role_dd],
477
+ outputs=[role_dd, action_dd, mapping_md, obs_md, raw_json, status_box],
478
  )
479
  state_btn.click(fn=get_state_sync, outputs=[raw_json])
480
 
uv.lock CHANGED
The diff for this file is too large to render. See raw diff