From b0d6131567151f42f69d8eef390453190e98a4d8 Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Thu, 23 Jul 2026 02:02:41 -0700 Subject: [PATCH 001/167] Security audit: refresh scan baselines for current dependency set (#7362) The pip scan-packages extras shard has been red on main because openai 2.47.0 changed the code inside five previously baselined findings, so their evidence hashes no longer matched the allowlist. The hf-stack shard was about to go red the same way: unsloth-zoo 2026.7.5 changed two baselined test files. All seven reopened findings were re-verified against the exact resolved archives before re-baselining: - openai/_base_client.py: while True in SyncPage.iter_pages, the pagination iterator. - openai/auth/_workload.py: Azure IMDS and GCP metadata token providers for the documented workload identity federation feature. - openai/resources/{beta/responses,realtime,responses}: while True in websocket __aiter__ event loops; the loop bodies gained reconnect handling in 2.47.0, which is what shifted the hashes. - unsloth-zoo tests/test_vision_collator_audio.py: asserts that an inline /tmp/a.wav path is passed through by the audio collator. - unsloth-zoo tests/test_gemma4_forced_float32_ple_dtype.py: compile()/exec() of the project's own generated Gemma4 PLE cast helper source in tests. No existing entries were removed. All three shards now exit 0 locally against the same requirement sets CI uses. --- scripts/scan_packages_baseline.json | 56 +++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/scripts/scan_packages_baseline.json b/scripts/scan_packages_baseline.json index 936f748a74..65b8d2b11c 100644 --- a/scripts/scan_packages_baseline.json +++ b/scripts/scan_packages_baseline.json @@ -1561,6 +1561,62 @@ "severity": "CRITICAL", "evidence": "L111: out = extract_audio_info(msgs({\"type\": \"audio\", key: \"/tmp/a.wav\"})) sha256:2efe23ffbe2b91b8403aec9b700736919b59e5ca770f8e1f5501651b44b7d398", "evidence_hash": "d416b79dd17b24214f3f7653ac01354507d7bf0fc464dee30a4a4b8998f063ba" + }, + { + "package": "openai", + "file": "openai/_base_client.py", + "check": "C2 polling/beaconing loop detected", + "severity": "CRITICAL", + "evidence": "L274: while True: sha256:90a38e5c1e26893c7c273354143612640e9a9c0f079d3e2b60612d79f24e80a6", + "evidence_hash": "1022e8e8649436ec64a98a9d9141d085452c49549fd2157b0278fc369a83ac66" + }, + { + "package": "openai", + "file": "openai/auth/_workload.py", + "check": "Accesses cloud metadata/IMDS AND makes network calls", + "severity": "CRITICAL", + "evidence": "IMDS: L97: url = \"http://169.254.169.254/metadata/identity/oauth2/token\" | L150: url = \"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity\"\nNetwork: L78: http_client: httpx.Client | None = None, | L109: with httpx.Client() as client: | L134: http_client: httpx.Client | None = None, | L156: with httpx.Client() as client: | L251: exchange_client = DefaultHttpx2Client(follow_redirects=False) if self._use_httpx2 else httpx.Client()", + "evidence_hash": "9717e51cb961dc14c458955d91a1e48e3753997346ecea0106bded3a8d64bfe0" + }, + { + "package": "openai", + "file": "openai/resources/beta/responses/responses.py", + "check": "C2 polling/beaconing loop detected", + "severity": "CRITICAL", + "evidence": "L4000: while True: sha256:f8ab538118daba9ec06e27399dbdc90a4521c3390e6a47a6348a1f180a83effd", + "evidence_hash": "31481ea83c687acc27144d72d3832d4fb98dd1c79fb5e0ddd85080de95997b9f" + }, + { + "package": "openai", + "file": "openai/resources/realtime/realtime.py", + "check": "C2 polling/beaconing loop detected", + "severity": "CRITICAL", + "evidence": "L311: while True: sha256:5b63313072aae9ca28677e03426513ccf12221e4f4e0ea6c31efbe09790633b5", + "evidence_hash": "05e1af469d651b51673763a7c4cdf759af9472fb627b7b470adc28cc237bd650" + }, + { + "package": "openai", + "file": "openai/resources/responses/responses.py", + "check": "C2 polling/beaconing loop detected", + "severity": "CRITICAL", + "evidence": "L3951: while True: sha256:d68ef896bf0743ca430cfacb9a3353da1f3b9c51c3a21b6450a07a32b55aa2ac", + "evidence_hash": "160eecdd79b521bffbe8476f782b69a0724c35d1b19376a7600807165fd54f9f" + }, + { + "package": "unsloth-zoo", + "file": "tests/test_gemma4_forced_float32_ple_dtype.py", + "check": "Advanced obfuscation (marshal/compile/zlib) + exec/eval", + "severity": "HIGH", + "evidence": "Obfusc: L277: compile(rewritten + _GEMMA4_PLE_CAST_HELPER, \"\", \"exec\") | L440: compile(on, \"\", \"exec\") | L468: compile(generated, \"\", \"exec\")\nExec: L19: exec(_GEMMA4_PLE_CAST_HELPER, namespace)", + "evidence_hash": "a85e24d8e7c431563cbd83b70f91a3b971abde0f37083d68e70984147960cc70" + }, + { + "package": "unsloth-zoo", + "file": "tests/test_vision_collator_audio.py", + "check": "Writes to /tmp and executes (staged dropper)", + "severity": "CRITICAL", + "evidence": "L111: out = extract_audio_info(msgs({\"type\": \"audio\", key: \"/tmp/a.wav\"})) sha256:022f81dd21acfc6a35a058de96132834c218404a9e37b3d09a7768a8c8f6c728", + "evidence_hash": "2d1e75446af120d9133a42aa8af426a839d3434d9dc109cc1d6c1b22ca1ddb75" } ] } From 5aedfd0b46b5a5efe506f94deb55ae39dd202860 Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Thu, 23 Jul 2026 02:13:51 -0700 Subject: [PATCH 002/167] Studio: always show the executed Python script in chat with a download option (#7240) The script the python tool runs was rendered inside a collapsible that closes when the run ends or the thread is reopened, so the code disappeared from the transcript and there was no way to save it. Render the script outside the collapsible so it stays visible, and add a Download button that saves it as script.py. Other tools and normal chat are unaffected. --- .../assistant-ui/tool-ui-python.tsx | 64 ++++++++++++++++--- 1 file changed, 54 insertions(+), 10 deletions(-) diff --git a/studio/frontend/src/components/assistant-ui/tool-ui-python.tsx b/studio/frontend/src/components/assistant-ui/tool-ui-python.tsx index 1b3dd22000..e3f4a0aafe 100644 --- a/studio/frontend/src/components/assistant-ui/tool-ui-python.tsx +++ b/studio/frontend/src/components/assistant-ui/tool-ui-python.tsx @@ -8,7 +8,7 @@ import { getAuthToken } from "@/features/auth/session"; import type { ToolCallMessagePartComponent } from "@assistant-ui/react"; import { useToolArgsStatus } from "@assistant-ui/react"; import { code as codePlugin } from "@streamdown/code"; -import { CodeIcon, CopyIcon } from "lucide-react"; +import { CodeIcon, CopyIcon, DownloadIcon } from "lucide-react"; import { Tick02Icon } from "@/lib/tick-icon"; import { HugeiconsIcon } from "@hugeicons/react"; import { Spinner } from "@/components/ui/spinner"; @@ -83,6 +83,41 @@ function CopyBtn({ text }: { text: string }) { ); } +/** Save the executed script as a .py file via a client-side Blob (no server file serving). */ +function DownloadBtn({ code, name = "script.py" }: { code: string; name?: string }) { + const download = useCallback(() => { + if (typeof document === "undefined") { + return; + } + try { + const blob = new Blob([code], { type: "text/x-python" }); + const url = URL.createObjectURL(blob); + const anchor = document.createElement("a"); + anchor.href = url; + anchor.download = name; + document.body.appendChild(anchor); + anchor.click(); + anchor.remove(); + // Revoke next tick, after the click consumes the URL. + setTimeout(() => URL.revokeObjectURL(url), 0); + } catch { + // Best-effort: never break the transcript over a download. + } + }, [code, name]); + + return ( + + ); +} + /** Syntax-highlighted code via Streamdown + shiki; inherits parent container. */ function HighlightedCode({ code: source, language }: { code: string; language: string }) { const markdown = useMemo( @@ -153,23 +188,32 @@ const PythonToolUIImpl: ToolCallMessagePartComponent = ({ const authToken = getAuthToken(); return ( - // Open when mounted mid-run so live output shows; collapsed from history. + // Run status and output collapse from history, but the script source is + // rendered outside ToolFallbackContent so it stays visible on reopen (#7165). + {code && ( +
+
+
+ + script + +
+ + +
+
+ +
+
+ )}
- {/* Code + copy */} - {code && ( -
- -
- )} - {code && } - {/* Output */} {isRunning ? ( <> From e143e1ce33b7c7cffcd25b0de85459698de2619e Mon Sep 17 00:00:00 2001 From: Long Yixing Date: Thu, 23 Jul 2026 17:14:20 +0800 Subject: [PATCH 003/167] feat(studio): Mac-aware training controls for MLX (optimizers, LoftQ, packing) (#7358) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(studio): offer MLX-supported optimizers on Apple Silicon The training form's optimizer dropdown only listed CUDA/bitsandbytes optimizers (adamw_8bit, paged variants, torch fused). On Apple Silicon the MLX trainer supports a different set (adamw, adam, lion, muon, sgd, adafactor) and remaps every bitsandbytes/torch name to plain AdamW, so the dropdown misrepresented what actually runs. Offer the MLX optimizer list when the device is a Mac, and derive the displayed value so the control is never blank: the shared CUDA default and the other bitsandbytes/torch options render as AdamW (exactly how the MLX backend normalizes them), while any other value is shown as-is so an unrecognized or non-canonical imported optimizer is never mislabeled. Non-Mac behavior is unchanged. The run-summary optimizer label now resolves from both lists. * feat(studio): show an MLX-appropriate optimizer tooltip on Apple Silicon The optimizer tooltip described "8-bit variants" and recommended "Fused" for vision models, neither of which is offered when training runs on MLX. On Apple Silicon, show a tooltip that matches the MLX optimizer set and notes that Lion typically needs a lower learning rate than AdamW. Copy-only: no change to the selected optimizer or the learning rate, and the non-Mac tooltip is unchanged. The new string is added to the English locale; other locales fall back to English until translated, matching how new keys are handled elsewhere. * fix(studio): label Mac CUDA-alias optimizers as AdamW in the run summary On Apple Silicon the run-configuration summary looked up the stored optimizer name directly, so a run that kept a CUDA/bitsandbytes default such as adamw_8bit was labeled "AdamW 8-bit" even though the picker shows "AdamW" and the MLX backend runs plain AdamW. Mirror the training form's derivation so those aliases are labeled AdamW in the summary too. Display-only: no change to the stored or submitted optimizer, and non-Mac summaries are unchanged. * feat(studio): disable LoftQ and sequence packing on Apple Silicon Neither LoftQ nor sequence packing is supported on MLX — the backend rejects LoftQ and the trainer silently forces packing off — yet the training form still offered both on Apple Silicon. Disable the LoftQ LoRA-init option (greyed and unclickable, with an inline "Not supported on Apple Silicon" note) and the "Enable packing" checkbox (greyed, with a tooltip explaining why), matching how the unsupported "Enable streaming" control is presented. Clearing effects reset a stale loftq/packing value to its default on Mac so the disabled controls never submit it. Non-Mac behavior is unchanged. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- studio/frontend/src/config/training.ts | 13 +++ .../studio/sections/params-section.tsx | 79 +++++++++++++++++-- .../studio/sections/progress-section.tsx | 17 +++- studio/frontend/src/i18n/locales/en.ts | 2 + tests/studio/playwright_ui_font_scale.py | 18 ++--- tests/studio/test_ui_font_scale_contract.py | 12 ++- 6 files changed, 115 insertions(+), 26 deletions(-) diff --git a/studio/frontend/src/config/training.ts b/studio/frontend/src/config/training.ts index 873e9aa203..10f7e93e5e 100644 --- a/studio/frontend/src/config/training.ts +++ b/studio/frontend/src/config/training.ts @@ -92,6 +92,19 @@ export const OPTIMIZER_OPTIONS: ReadonlyArray<{ value: string; label: string }> { value: "adamw_torch_fused", label: "AdamW (PyTorch Fused)" }, ]; +// Optimizers the MLX trainer actually supports on Apple Silicon. Values must +// match SUPPORTED_MLX_OPTIMIZERS in unsloth-zoo's mlx/trainer.py; on MLX the +// bitsandbytes/torch names above have no meaning and are remapped to plain +// AdamW, so Studio offers this list instead when running on a Mac. +export const MLX_OPTIMIZER_OPTIONS: ReadonlyArray<{ value: string; label: string }> = [ + { value: "adamw", label: "AdamW" }, + { value: "adam", label: "Adam" }, + { value: "lion", label: "Lion" }, + { value: "muon", label: "Muon" }, + { value: "sgd", label: "SGD" }, + { value: "adafactor", label: "Adafactor" }, +]; + export const LR_SCHEDULER_OPTIONS: ReadonlyArray<{ value: string; label: string }> = [ { value: "linear", label: "Linear" }, { value: "cosine", label: "Cosine" }, diff --git a/studio/frontend/src/features/studio/sections/params-section.tsx b/studio/frontend/src/features/studio/sections/params-section.tsx index 029fef4e19..3270eb4e3d 100644 --- a/studio/frontend/src/features/studio/sections/params-section.tsx +++ b/studio/frontend/src/features/studio/sections/params-section.tsx @@ -36,6 +36,7 @@ import { CONTEXT_LENGTHS, CPT_TARGET_MODULES, LR_SCHEDULER_OPTIONS, + MLX_OPTIMIZER_OPTIONS, OPTIMIZER_OPTIONS, TARGET_MODULES, } from "@/config/training"; @@ -204,6 +205,42 @@ export function ParamsSection(): ReactElement { setCtxInput(String(store.contextLength)); }, [store.contextLength]); + // On Apple Silicon the MLX trainer supports a different optimizer set than + // the CUDA/bitsandbytes list, so offer the MLX names there. + const isMac = platformDeviceType === "mac"; + const optimizerOptions = isMac ? MLX_OPTIMIZER_OPTIONS : OPTIMIZER_OPTIONS; + + // On Mac, the MLX backend normalizes every CUDA/bitsandbytes optimizer in + // OPTIMIZER_OPTIONS (including the shared default) to plain AdamW, so show + // AdamW for those to keep the control truthful and non-blank. Any other + // value -- an MLX optimizer the user picked, or an unrecognized/non-canonical + // imported one -- is shown as-is rather than mislabeled as AdamW, since the + // backend would run or reject it on its own terms. Non-Mac display unchanged. + const isCudaAliasOptimizer = OPTIMIZER_OPTIONS.some( + (o) => o.value === store.optimizerType, + ); + const selectedOptimizer = + isMac && isCudaAliasOptimizer ? "adamw" : store.optimizerType; + + // LoftQ is not supported on MLX (the backend rejects it), so clear a stale + // selection to lora on Apple Silicon -- whether persisted, applied from a + // model default, or imported -- so the backend never receives it. + const setLoraVariant = store.setLoraVariant; + useEffect(() => { + if (isMac && store.loraVariant === "loftq") { + setLoraVariant("lora"); + } + }, [isMac, store.loraVariant, setLoraVariant]); + + // Packing is not supported on MLX (the backend forces it off), so clear it on + // Apple Silicon -- the checkbox is disabled and the flag is never sent. + const setPacking = store.setPacking; + useEffect(() => { + if (isMac && store.packing) { + setPacking(false); + } + }, [isMac, store.packing, setPacking]); + const trySetContextLength = (input: string): number | null => { const n = Number(input); if (Number.isInteger(n) && n > 0) { @@ -706,8 +743,9 @@ export function ParamsSection(): ReactElement { ))} @@ -765,7 +805,11 @@ export function ParamsSection(): ReactElement { label={t("studio.params.optimizer")} tooltip={ <> - {t("studio.params.optimizerTooltip")}{" "} + {t( + isMac + ? "studio.params.optimizerTooltipMlx" + : "studio.params.optimizerTooltip", + )}{" "} (KV_CACHE_DTYPES); export const SPECULATIVE_TYPES = [ From d17567af3e5c3ac03a0379bcf69a8c405d87d079 Mon Sep 17 00:00:00 2001 From: Souravrajvi0 <144546710+Souravrajvi0@users.noreply.github.com> Date: Fri, 24 Jul 2026 14:53:24 +0530 Subject: [PATCH 031/167] fix(studio/colab): restore blank Colab iframe embed (#7344) (#7349) * fix(studio/colab): restore iframe embed via serve_kernel_port_as_iframe Colab's output sanitizer often strips custom