From 0a16db0f53565f1a90f96f4d23bcc1c38c8def78 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Sun, 28 Jun 2026 00:19:53 +0000 Subject: [PATCH] fix(dashboard): probe the interpreter from the repo root in gen:api The version guard ran execFileSync without a cwd, so a repo-root-relative LITELLM_PYTHON (e.g. .venv/bin/python) was resolved against the dashboard directory the npm script runs in, while the later spec dump resolves it against the repo root. Thread repoRoot into the probe so both calls agree. --- .../scripts/gen-api-types.mjs | 13 ++++++--- .../tests/gen-api-types.test.ts | 27 +++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/ui/litellm-dashboard/scripts/gen-api-types.mjs b/ui/litellm-dashboard/scripts/gen-api-types.mjs index 974a391b747..fb5ae803e50 100644 --- a/ui/litellm-dashboard/scripts/gen-api-types.mjs +++ b/ui/litellm-dashboard/scripts/gen-api-types.mjs @@ -55,12 +55,17 @@ export function unsupportedPythonMessage(pythonCommand, version) { ); } -function assertSupportedPython(pythonCommand) { +export function probePythonVersion(pythonCommand, cwd, exec = execFileSync) { const probe = "import sys; print('.'.join(map(str, sys.version_info[:2])))"; - const stdout = execFileSync(pythonCommand[0], [...pythonCommand.slice(1), "-c", probe], { + const stdout = exec(pythonCommand[0], [...pythonCommand.slice(1), "-c", probe], { + cwd, encoding: "utf8", }); - const version = parsePythonVersion(stdout); + return parsePythonVersion(stdout); +} + +function assertSupportedPython(pythonCommand, cwd) { + const version = probePythonVersion(pythonCommand, cwd); if (!isSupportedPython(version)) { throw new Error(unsupportedPythonMessage(pythonCommand, version)); } @@ -89,7 +94,7 @@ function main() { ].join("\n"); try { - assertSupportedPython(python); + assertSupportedPython(python, repoRoot); execFileSync(python[0], [...python.slice(1), "-c", dumpSpec, specPath], { cwd: repoRoot, diff --git a/ui/litellm-dashboard/tests/gen-api-types.test.ts b/ui/litellm-dashboard/tests/gen-api-types.test.ts index f6946b87632..412a340767d 100644 --- a/ui/litellm-dashboard/tests/gen-api-types.test.ts +++ b/ui/litellm-dashboard/tests/gen-api-types.test.ts @@ -2,6 +2,7 @@ import { describe, it, expect } from "vitest"; import { resolvePythonCommand, parsePythonVersion, + probePythonVersion, isSupportedPython, unsupportedPythonMessage, } from "../scripts/gen-api-types.mjs"; @@ -49,6 +50,32 @@ describe("parsePythonVersion", () => { }); }); +describe("probePythonVersion", () => { + it("probes the interpreter from the repo root, matching the spec-dump cwd", () => { + let seenCwd: string | undefined; + const exec = (_bin: string, _args: string[], opts: { cwd?: string }) => { + seenCwd = opts.cwd; + return "3.11\n"; + }; + const version = probePythonVersion(["python3"], repoRoot, exec); + expect(seenCwd).toBe(repoRoot); + expect(version).toEqual({ major: 3, minor: 11 }); + }); + + it("resolves a repo-root-relative interpreter against the repo root, not the dashboard cwd", () => { + let seenBin: string | undefined; + let seenCwd: string | undefined; + const exec = (bin: string, _args: string[], opts: { cwd?: string }) => { + seenBin = bin; + seenCwd = opts.cwd; + return "3.12\n"; + }; + probePythonVersion([".venv/bin/python"], repoRoot, exec); + expect(seenBin).toBe(".venv/bin/python"); + expect(seenCwd).toBe(repoRoot); + }); +}); + describe("isSupportedPython", () => { it("rejects the 3.9 interpreter that breaks on dataclass slots=True", () => { expect(isSupportedPython({ major: 3, minor: 9 })).toBe(false);