Skip to content

Commit 1f8e236

Browse files
Merge branch 'dev' into Layer3-PR8
2 parents b7e1ad7 + 8ef4d27 commit 1f8e236

42 files changed

Lines changed: 1751 additions & 103 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/deploy-docs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
working-directory: ./website
2727
steps:
2828
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
29-
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
29+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
3030
with:
3131
node-version: 24
3232
cache: npm

.github/workflows/process-changes.yml

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
- name: "Checkout Repository"
1414
uses: actions/checkout@v7.0.0
1515
- name: "Install Node"
16-
uses: actions/setup-node@v6.4.0
16+
uses: actions/setup-node@v7.0.0
1717
with:
1818
node-version: '24'
1919
- name: "Install Dependencies"
@@ -148,6 +148,76 @@ jobs:
148148
name: cypress-screenshots-visual
149149
path: cypress/screenshots
150150

151+
playwright-visual-regression-test-init:
152+
name: "Initialize Playwright Visual Regression Test"
153+
runs-on: ubuntu-latest
154+
needs: install-and-build
155+
steps:
156+
- name: "Checkout Repository"
157+
uses: actions/checkout@v7.0.0
158+
159+
- name: "Save current Head-Ref as PR-branch"
160+
shell: bash
161+
run: git checkout -B PR-HEAD
162+
163+
- uses: ./.github/actions/prepare
164+
with:
165+
usebasebranch: true
166+
loadprbuild: false
167+
168+
- name: "Checkout Playwright tests from Head-Ref"
169+
shell: bash
170+
run: |
171+
git checkout PR-HEAD -- ./playwright
172+
git checkout PR-HEAD -- ./example
173+
git checkout PR-HEAD -- ./package.json
174+
git checkout PR-HEAD -- ./playwright.config.js
175+
git checkout PR-HEAD -- ./playwright.init.config.js
176+
git checkout PR-HEAD -- ./playwright.visual.config.js
177+
178+
- name: "Install PR npm dependencies"
179+
run: ELECTRON_SKIP_BINARY_DOWNLOAD=1 npm install
180+
181+
- name: "Install Playwright Chromium"
182+
run: npx playwright install --with-deps chromium
183+
184+
- name: "Generate Playwright baseline screenshots"
185+
run: npm run test:pw:init
186+
187+
- uses: actions/upload-artifact@v7
188+
with:
189+
name: playwright-init-screenshots
190+
path: playwright/screenshots_init
191+
192+
playwright-visual-regression-test:
193+
name: "Playwright Visual Regression Test"
194+
runs-on: ubuntu-latest
195+
needs: playwright-visual-regression-test-init
196+
steps:
197+
- name: "Checkout Repository"
198+
uses: actions/checkout@v7.0.0
199+
200+
- uses: ./.github/actions/prepare
201+
202+
- name: "Install Playwright Chromium"
203+
run: npx playwright install --with-deps chromium
204+
205+
- uses: actions/download-artifact@v8
206+
with:
207+
name: playwright-init-screenshots
208+
path: playwright/screenshots_init
209+
210+
- name: "Run Playwright visual regression tests"
211+
run: npm run test:pw:visual
212+
213+
- uses: actions/upload-artifact@v7
214+
if: failure()
215+
with:
216+
name: playwright-screenshots-visual
217+
path: |
218+
playwright/test-results/
219+
playwright/playwright-report/
220+
151221
functional-test:
152222
name: "Functional Test (Websocket)"
153223
runs-on: ubuntu-latest

.github/workflows/pypi.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
generate_release_notes: true
3939
- name: Set up Python
4040
if: ${{ steps.version.outputs.TAG_EXISTS == '0' }}
41-
uses: actions/setup-python@v6
41+
uses: actions/setup-python@v7
4242
with:
4343
python-version: '3.x'
4444
- name: Install dependencies

.github/workflows/python-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
- name: Checkout Repository
2020
uses: actions/checkout@v7.0.0
2121
- name: Set up Python
22-
uses: actions/setup-python@v6
22+
uses: actions/setup-python@v7
2323
with:
2424
python-version: ${{ matrix.python-version }}
2525
- name: Install package and test dependencies

.github/workflows/update-js-build-files.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
# see: https://stackoverflow.com/questions/57921401/push-to-origin-from-github-action/58393457#58393457
2323
# with:
2424
# token: ${{ secrets.PAT }}
25-
- uses: actions/setup-node@v6.4.0
25+
- uses: actions/setup-node@v7.0.0
2626
with:
2727
node-version: '24'
2828
- run: ELECTRON_SKIP_BINARY_DOWNLOAD=1 npm install

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ cypress/screenshots
22
cypress/screenshots_init
33
cypress/downloads
44
cypress/fixtures
5+
playwright/screenshots
6+
playwright/screenshots_init
7+
playwright/screenshots_diff
58
node_modules
69
build
710
th/CMakeLists.txt

README.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,32 @@ VISDOM_USE_ENV_CREDENTIALS=1 visdom -enable_login
227227
You can also use `VISDOM_COOKIE` variable to provide cookies value if the cookie file wasn't generated, or the
228228
flag `-force_new_cookie` was set.
229229

230+
#### HTTPS Support
231+
232+
To run the visdom server over HTTPS,user need to provide an SSL certificate and key file:
233+
234+
```bash
235+
# Generate a self-signed certificate (for development only)
236+
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
237+
238+
# Start the server with HTTPS
239+
python -m visdom.server -ssl_certfile cert.pem -ssl_keyfile key.pem
240+
```
241+
242+
Access the server at `https://localhost:8097`.
243+
244+
Connect via the Python client:
245+
```python
246+
# For Production - real CA-signed certificate (default)
247+
vis = visdom.Visdom(server="https://myserver.com")
248+
249+
# For Development - self signed certificate
250+
vis = visdom.Visdom(server="https://localhost", ssl_verify=False)
251+
```
252+
253+
> **Note**: `ssl_verify=False` disables certificate verification and should only be used in development with self-signed certificates. Do not use in production.
254+
255+
230256
#### Python example
231257
```python
232258
import visdom
@@ -391,6 +417,49 @@ with VisdomLogger(viz, env="my_run", log_every=50) as tracker:
391417

392418
Each unique name passed to `tracker.log()` gets its own window. The first call creates it; subsequent calls append. See `example/train_example.py` for a full working example.
393419

420+
### scikit-learn
421+
422+
`visdom.loggers.VisdomSklearnLogger` patches all sklearn `fit()` calls so every estimator trained after `autolog()` logs to Visdom automatically — no per-estimator code needed.
423+
424+
**Plain estimators** (classifiers, regressors, clusterers) produce a text pane with the estimator name, dataset shape, training score, fit time, and all hyperparameters.
425+
426+
**GridSearchCV / RandomizedSearchCV** produce a bar chart of `mean_test_score` per parameter combination and a text pane with `best_score_`, `best_params_`, and fit time.
427+
428+
```python
429+
import visdom
430+
from visdom.loggers import VisdomSklearnLogger
431+
from sklearn.ensemble import RandomForestClassifier
432+
from sklearn.linear_model import Ridge
433+
from sklearn.model_selection import GridSearchCV
434+
435+
viz = visdom.Visdom()
436+
VisdomSklearnLogger.autolog()
437+
438+
clf = RandomForestClassifier(n_estimators=100)
439+
clf.fit(X_train, y_train) # -> text pane
440+
441+
reg = Ridge(alpha=1.0)
442+
reg.fit(X_train, y_train) # -> text pane
443+
444+
gs = GridSearchCV(clf, param_grid, cv=3)
445+
gs.fit(X_train, y_train) # -> bar chart + text pane
446+
```
447+
448+
If you have a custom Visdom connection (non-default port, remote server, auth), pass it explicitly:
449+
450+
```python
451+
import visdom
452+
453+
viz = visdom.Visdom(port=8098, server="http://myserver")
454+
VisdomSklearnLogger.autolog(viz, env="sklearn_run")
455+
```
456+
457+
**Parameters:**
458+
- `viz`: a connected `visdom.Visdom()` instance (optional — created internally if not passed)
459+
- `env`: environment name (default: `viz.env` if set, otherwise auto-generated from timestamp)
460+
461+
See `example/train_sklearn_example.py` for a full working example covering plain estimators and grid search.
462+
394463
## Details
395464
<img src="https://user-images.githubusercontent.com/19650074/198747904-7a8a580f-851a-45fb-8f45-94e54a910ee2.png"/>
396465

download.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ wget https://unpkg.com/jquery@3.1.1/dist/jquery.min.js -O py/static/js/jquery.mi
55
wget https://unpkg.com/bootstrap@3.3.7/dist/js/bootstrap.min.js -O py/static/js/bootstrap.min.js
66
wget https://unpkg.com/react@16.2.0/umd/react.production.min.js -O py/static/js/react-react.min.js
77
wget https://unpkg.com/react-dom@16.2.0/umd/react-dom.production.min.js -O py/static/js/react-dom.min.js
8-
wget "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_SVG" -O py/static/js/mathjax-MathJax.js
8+
wget "https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-mml-svg.js" -O py/static/js/mathjax-tex-mml-svg.js
99
wget https://cdn.rawgit.com/plotly/plotly.js/master/dist/plotly.min.js -O py/static/js/plotly-plotly.min.js
1010
wget https://unpkg.com/sjcl@1.0.7/sjcl.js -O py/static/js/sjcl.js
1111
wget https://cdnjs.cloudflare.com/ajax/libs/react-modal/3.6.1/react-modal.min.js -o py/static/js/react-modal.min.js

example/train_sklearn_example.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from sklearn.datasets import make_classification, make_regression
2+
from sklearn.ensemble import RandomForestClassifier
3+
from sklearn.linear_model import Ridge
4+
from sklearn.model_selection import GridSearchCV, train_test_split
5+
6+
import visdom
7+
from visdom.loggers import VisdomSklearnLogger
8+
9+
10+
def main():
11+
# synthetic classification: 500 samples, 20 features, 2 classes
12+
X_clf, y_clf = make_classification(
13+
n_samples=500,
14+
n_features=20,
15+
n_informative=10,
16+
random_state=42,
17+
)
18+
X_train_clf, X_test_clf, y_train_clf, y_test_clf = train_test_split(
19+
X_clf, y_clf, test_size=0.2, random_state=42
20+
)
21+
22+
# synthetic regression: 500 samples, 20 features
23+
X_reg, y_reg = make_regression(
24+
n_samples=500,
25+
n_features=20,
26+
noise=0.1,
27+
random_state=42,
28+
)
29+
X_train_reg, X_test_reg, y_train_reg, y_test_reg = train_test_split(
30+
X_reg, y_reg, test_size=0.2, random_state=42
31+
)
32+
33+
viz = visdom.Visdom()
34+
VisdomSklearnLogger.autolog(viz, env="sklearn_run")
35+
36+
# plain classifier -> text pane (dataset, train_score, fit_time, params)
37+
clf = RandomForestClassifier(n_estimators=100, random_state=42)
38+
clf.fit(X_train_clf, y_train_clf)
39+
40+
# plain regressor -> text pane (dataset, train_score, fit_time, params)
41+
reg = Ridge(alpha=1.0)
42+
reg.fit(X_train_reg, y_train_reg)
43+
44+
# grid search -> bar chart of mean_test_score + best params text pane
45+
param_grid = {
46+
"n_estimators": [50, 100, 200],
47+
"max_depth": [3, 5, None],
48+
}
49+
gs = GridSearchCV(
50+
RandomForestClassifier(random_state=42),
51+
param_grid,
52+
cv=3,
53+
scoring="accuracy",
54+
)
55+
gs.fit(X_train_clf, y_train_clf)
56+
57+
print("RF accuracy: {:.4f}".format(clf.score(X_test_clf, y_test_clf)))
58+
print("Ridge R2: {:.4f}".format(reg.score(X_test_reg, y_test_reg)))
59+
print("GridSearch best: {:.4f} params: {}".format(gs.best_score_, gs.best_params_))
60+
61+
62+
if __name__ == "__main__":
63+
main()

js/api/ApiProvider.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,18 @@ const ApiProvider = ({ children }) => {
336336
});
337337
};
338338

339+
const sendCommentUpdate = (envID, win, comment) => {
340+
if (win === null || sessionInfo.readonly) {
341+
return;
342+
}
343+
sendSocketMessage({
344+
cmd: 'update_comment',
345+
eid: envID,
346+
win: win,
347+
data: comment,
348+
});
349+
};
350+
339351
// Save layout lists to the server
340352
const sendLayoutsSave = (layoutLists) => {
341353
// pushes layouts to the server
@@ -387,6 +399,7 @@ const ApiProvider = ({ children }) => {
387399
value={{
388400
apiHandlers,
389401
connected,
402+
sendCommentUpdate,
390403
sendEmbeddingPop,
391404
sendEnvDelete,
392405
sendEnvQuery,

0 commit comments

Comments
 (0)