fix(runtime): 本机估计运行时 - 兼容 Windows 路径覆盖并允许零人数姿态结果
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
ed676046d6
commit
7105b04967
5 files changed
+37
-14
No files matched your search
@@ -6,6 +6,7 @@ import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process";
|
|||||||
import type { Request, RequestHandler, Response } from "express";
|
import type { Request, RequestHandler, Response } from "express";
|
||||||
|
|
||||||
import type { LocalRuntimeModule } from "../local-runtime.js";
|
import type { LocalRuntimeModule } from "../local-runtime.js";
|
||||||
|
import { resolveEnvPath } from "./runtime-env-path.js";
|
||||||
|
|
||||||
const DEFAULT_MODEL_ID = "depth-anything/Depth-Anything-V2-Small-hf";
|
const DEFAULT_MODEL_ID = "depth-anything/Depth-Anything-V2-Small-hf";
|
||||||
const MAX_INPUT_BYTES = 12 * 1024 * 1024;
|
const MAX_INPUT_BYTES = 12 * 1024 * 1024;
|
||||||
@@ -103,12 +104,12 @@ export function resolveDepthRuntimePaths(env: NodeJS.ProcessEnv = process.env):
|
|||||||
const moduleRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
const moduleRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
||||||
const configuredProjectRoot = env.CANVAS_PROJECT_ROOT?.trim();
|
const configuredProjectRoot = env.CANVAS_PROJECT_ROOT?.trim();
|
||||||
const projectRoots = [
|
const projectRoots = [
|
||||||
configuredProjectRoot ? path.resolve(configuredProjectRoot) : undefined,
|
configuredProjectRoot ? resolveEnvPath(configuredProjectRoot) : undefined,
|
||||||
path.resolve(moduleRoot, ".."),
|
path.resolve(moduleRoot, ".."),
|
||||||
path.resolve(process.cwd()),
|
path.resolve(process.cwd()),
|
||||||
].filter((value): value is string => Boolean(value));
|
].filter((value): value is string => Boolean(value));
|
||||||
const projectRoot = configuredProjectRoot
|
const projectRoot = configuredProjectRoot
|
||||||
? path.resolve(configuredProjectRoot)
|
? resolveEnvPath(configuredProjectRoot)
|
||||||
: projectRoots.find((value) => fs.existsSync(path.join(value, ".local"))) || projectRoots[0]!;
|
: projectRoots.find((value) => fs.existsSync(path.join(value, ".local"))) || projectRoots[0]!;
|
||||||
const scriptCandidates = [
|
const scriptCandidates = [
|
||||||
path.join(moduleRoot, "python", "depth_anything_runtime.py"),
|
path.join(moduleRoot, "python", "depth_anything_runtime.py"),
|
||||||
@@ -116,12 +117,12 @@ export function resolveDepthRuntimePaths(env: NodeJS.ProcessEnv = process.env):
|
|||||||
];
|
];
|
||||||
const scriptPath = scriptCandidates.find((value) => fs.existsSync(value)) || scriptCandidates[0]!;
|
const scriptPath = scriptCandidates.find((value) => fs.existsSync(value)) || scriptCandidates[0]!;
|
||||||
const pythonCandidates = env.CANVAS_DEPTH_PYTHON?.trim()
|
const pythonCandidates = env.CANVAS_DEPTH_PYTHON?.trim()
|
||||||
? [path.resolve(env.CANVAS_DEPTH_PYTHON.trim())]
|
? [resolveEnvPath(env.CANVAS_DEPTH_PYTHON)]
|
||||||
: process.platform === "win32"
|
: process.platform === "win32"
|
||||||
? [path.join(projectRoot, ".local", "depth-anything-v2", "venv", "Scripts", "python.exe")]
|
? [path.join(projectRoot, ".local", "depth-anything-v2", "venv", "Scripts", "python.exe")]
|
||||||
: [path.join(projectRoot, ".local", "depth-anything-v2", "venv", "bin", "python")];
|
: [path.join(projectRoot, ".local", "depth-anything-v2", "venv", "bin", "python")];
|
||||||
const pythonPath = pythonCandidates[0]!;
|
const pythonPath = pythonCandidates[0]!;
|
||||||
const hfHome = path.resolve(env.CANVAS_DEPTH_HF_HOME?.trim() || path.join(projectRoot, ".local", "cache", "huggingface"));
|
const hfHome = resolveEnvPath(env.CANVAS_DEPTH_HF_HOME?.trim() || path.join(projectRoot, ".local", "cache", "huggingface"));
|
||||||
const modelId = env.CANVAS_DEPTH_MODEL_ID?.trim() || DEFAULT_MODEL_ID;
|
const modelId = env.CANVAS_DEPTH_MODEL_ID?.trim() || DEFAULT_MODEL_ID;
|
||||||
const modelCacheDir = path.join(hfHome, "hub", `models--${modelId.replaceAll("/", "--")}`);
|
const modelCacheDir = path.join(hfHome, "hub", `models--${modelId.replaceAll("/", "--")}`);
|
||||||
const workerExists = fs.existsSync(scriptPath);
|
const workerExists = fs.existsSync(scriptPath);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process";
|
|||||||
import type { Request, RequestHandler, Response } from "express";
|
import type { Request, RequestHandler, Response } from "express";
|
||||||
|
|
||||||
import type { LocalRuntimeModule } from "../local-runtime.js";
|
import type { LocalRuntimeModule } from "../local-runtime.js";
|
||||||
|
import { resolveEnvPath } from "./runtime-env-path.js";
|
||||||
|
|
||||||
const DEFAULT_MODEL_ID = "lllyasviel/Annotators";
|
const DEFAULT_MODEL_ID = "lllyasviel/Annotators";
|
||||||
const MAX_INPUT_BYTES = 12 * 1024 * 1024;
|
const MAX_INPUT_BYTES = 12 * 1024 * 1024;
|
||||||
@@ -103,12 +104,12 @@ export function resolveLineartRuntimePaths(env: NodeJS.ProcessEnv = process.env)
|
|||||||
const moduleRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
const moduleRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
||||||
const configuredProjectRoot = env.CANVAS_PROJECT_ROOT?.trim();
|
const configuredProjectRoot = env.CANVAS_PROJECT_ROOT?.trim();
|
||||||
const projectRoots = [
|
const projectRoots = [
|
||||||
configuredProjectRoot ? path.resolve(configuredProjectRoot) : undefined,
|
configuredProjectRoot ? resolveEnvPath(configuredProjectRoot) : undefined,
|
||||||
path.resolve(moduleRoot, ".."),
|
path.resolve(moduleRoot, ".."),
|
||||||
path.resolve(process.cwd()),
|
path.resolve(process.cwd()),
|
||||||
].filter((value): value is string => Boolean(value));
|
].filter((value): value is string => Boolean(value));
|
||||||
const projectRoot = configuredProjectRoot
|
const projectRoot = configuredProjectRoot
|
||||||
? path.resolve(configuredProjectRoot)
|
? resolveEnvPath(configuredProjectRoot)
|
||||||
: projectRoots.find((value) => fs.existsSync(path.join(value, ".local"))) || projectRoots[0]!;
|
: projectRoots.find((value) => fs.existsSync(path.join(value, ".local"))) || projectRoots[0]!;
|
||||||
const scriptCandidates = [
|
const scriptCandidates = [
|
||||||
path.join(moduleRoot, "python", "lineart_runtime.py"),
|
path.join(moduleRoot, "python", "lineart_runtime.py"),
|
||||||
@@ -116,12 +117,12 @@ export function resolveLineartRuntimePaths(env: NodeJS.ProcessEnv = process.env)
|
|||||||
];
|
];
|
||||||
const scriptPath = scriptCandidates.find((value) => fs.existsSync(value)) || scriptCandidates[0]!;
|
const scriptPath = scriptCandidates.find((value) => fs.existsSync(value)) || scriptCandidates[0]!;
|
||||||
const pythonCandidates = env.CANVAS_DEPTH_PYTHON?.trim()
|
const pythonCandidates = env.CANVAS_DEPTH_PYTHON?.trim()
|
||||||
? [path.resolve(env.CANVAS_DEPTH_PYTHON.trim())]
|
? [resolveEnvPath(env.CANVAS_DEPTH_PYTHON)]
|
||||||
: process.platform === "win32"
|
: process.platform === "win32"
|
||||||
? [path.join(projectRoot, ".local", "depth-anything-v2", "venv", "Scripts", "python.exe")]
|
? [path.join(projectRoot, ".local", "depth-anything-v2", "venv", "Scripts", "python.exe")]
|
||||||
: [path.join(projectRoot, ".local", "depth-anything-v2", "venv", "bin", "python")];
|
: [path.join(projectRoot, ".local", "depth-anything-v2", "venv", "bin", "python")];
|
||||||
const pythonPath = pythonCandidates[0]!;
|
const pythonPath = pythonCandidates[0]!;
|
||||||
const hfHome = path.resolve(env.CANVAS_LINEART_HF_HOME?.trim() || env.CANVAS_DEPTH_HF_HOME?.trim() || path.join(projectRoot, ".local", "cache", "huggingface"));
|
const hfHome = resolveEnvPath(env.CANVAS_LINEART_HF_HOME?.trim() || env.CANVAS_DEPTH_HF_HOME?.trim() || path.join(projectRoot, ".local", "cache", "huggingface"));
|
||||||
const modelId = env.CANVAS_LINEART_MODEL_ID?.trim() || DEFAULT_MODEL_ID;
|
const modelId = env.CANVAS_LINEART_MODEL_ID?.trim() || DEFAULT_MODEL_ID;
|
||||||
const modelCacheDir = path.join(hfHome, "hub", `models--${modelId.replaceAll("/", "--")}`);
|
const modelCacheDir = path.join(hfHome, "hub", `models--${modelId.replaceAll("/", "--")}`);
|
||||||
const workerExists = fs.existsSync(scriptPath);
|
const workerExists = fs.existsSync(scriptPath);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process";
|
|||||||
import type { Request, RequestHandler, Response } from "express";
|
import type { Request, RequestHandler, Response } from "express";
|
||||||
|
|
||||||
import type { LocalRuntimeModule } from "../local-runtime.js";
|
import type { LocalRuntimeModule } from "../local-runtime.js";
|
||||||
|
import { resolveEnvPath } from "./runtime-env-path.js";
|
||||||
|
|
||||||
const DEFAULT_MODEL_ID = "lllyasviel/Annotators";
|
const DEFAULT_MODEL_ID = "lllyasviel/Annotators";
|
||||||
const DEFAULT_BACKEND = "openpose-body" as const;
|
const DEFAULT_BACKEND = "openpose-body" as const;
|
||||||
@@ -108,12 +109,12 @@ export function resolvePoseRuntimePaths(env: NodeJS.ProcessEnv = process.env): P
|
|||||||
const moduleRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
const moduleRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
||||||
const configuredProjectRoot = env.CANVAS_PROJECT_ROOT?.trim();
|
const configuredProjectRoot = env.CANVAS_PROJECT_ROOT?.trim();
|
||||||
const projectRoots = [
|
const projectRoots = [
|
||||||
configuredProjectRoot ? path.resolve(configuredProjectRoot) : undefined,
|
configuredProjectRoot ? resolveEnvPath(configuredProjectRoot) : undefined,
|
||||||
path.resolve(moduleRoot, ".."),
|
path.resolve(moduleRoot, ".."),
|
||||||
path.resolve(process.cwd()),
|
path.resolve(process.cwd()),
|
||||||
].filter((value): value is string => Boolean(value));
|
].filter((value): value is string => Boolean(value));
|
||||||
const projectRoot = configuredProjectRoot
|
const projectRoot = configuredProjectRoot
|
||||||
? path.resolve(configuredProjectRoot)
|
? resolveEnvPath(configuredProjectRoot)
|
||||||
: projectRoots.find((value) => fs.existsSync(path.join(value, ".local"))) || projectRoots[0]!;
|
: projectRoots.find((value) => fs.existsSync(path.join(value, ".local"))) || projectRoots[0]!;
|
||||||
const scriptCandidates = [
|
const scriptCandidates = [
|
||||||
path.join(moduleRoot, "python", "pose_runtime.py"),
|
path.join(moduleRoot, "python", "pose_runtime.py"),
|
||||||
@@ -121,12 +122,12 @@ export function resolvePoseRuntimePaths(env: NodeJS.ProcessEnv = process.env): P
|
|||||||
];
|
];
|
||||||
const scriptPath = scriptCandidates.find((value) => fs.existsSync(value)) || scriptCandidates[0]!;
|
const scriptPath = scriptCandidates.find((value) => fs.existsSync(value)) || scriptCandidates[0]!;
|
||||||
const pythonCandidates = env.CANVAS_POSE_PYTHON?.trim()
|
const pythonCandidates = env.CANVAS_POSE_PYTHON?.trim()
|
||||||
? [path.resolve(env.CANVAS_POSE_PYTHON.trim())]
|
? [resolveEnvPath(env.CANVAS_POSE_PYTHON)]
|
||||||
: process.platform === "win32"
|
: process.platform === "win32"
|
||||||
? [path.join(projectRoot, ".local", "depth-anything-v2", "venv", "Scripts", "python.exe")]
|
? [path.join(projectRoot, ".local", "depth-anything-v2", "venv", "Scripts", "python.exe")]
|
||||||
: [path.join(projectRoot, ".local", "depth-anything-v2", "venv", "bin", "python")];
|
: [path.join(projectRoot, ".local", "depth-anything-v2", "venv", "bin", "python")];
|
||||||
const pythonPath = pythonCandidates[0]!;
|
const pythonPath = pythonCandidates[0]!;
|
||||||
const hfHome = path.resolve(
|
const hfHome = resolveEnvPath(
|
||||||
env.CANVAS_POSE_HF_HOME?.trim()
|
env.CANVAS_POSE_HF_HOME?.trim()
|
||||||
|| env.CANVAS_LINEART_HF_HOME?.trim()
|
|| env.CANVAS_LINEART_HF_HOME?.trim()
|
||||||
|| env.CANVAS_DEPTH_HF_HOME?.trim()
|
|| env.CANVAS_DEPTH_HF_HOME?.trim()
|
||||||
@@ -278,7 +279,7 @@ export class PoseEstimationWorker {
|
|||||||
|| typeof value.modelId !== "string"
|
|| typeof value.modelId !== "string"
|
||||||
|| value.device !== "cpu"
|
|| value.device !== "cpu"
|
||||||
|| value.backend !== DEFAULT_BACKEND
|
|| value.backend !== DEFAULT_BACKEND
|
||||||
|| !isPositiveInteger(value.personCount)
|
|| !isNonNegativeInteger(value.personCount)
|
||||||
|| typeof value.pngBase64 !== "string"
|
|| typeof value.pngBase64 !== "string"
|
||||||
|| !value.pngBase64) {
|
|| !value.pngBase64) {
|
||||||
pending.reject(new PoseRuntimeError("pose_response_invalid", "本机姿态响应无效", 500));
|
pending.reject(new PoseRuntimeError("pose_response_invalid", "本机姿态响应无效", 500));
|
||||||
@@ -355,3 +356,7 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|||||||
function isPositiveInteger(value: unknown): value is number {
|
function isPositiveInteger(value: unknown): value is number {
|
||||||
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isNonNegativeInteger(value: unknown): value is number {
|
||||||
|
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import path from "node:path";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve env-configured filesystem paths without mangling Windows absolutes on POSIX hosts.
|
||||||
|
* `path.resolve("C:\\foo")` on macOS/Linux incorrectly joins cwd, which breaks overrides and tests.
|
||||||
|
*/
|
||||||
|
export function resolveEnvPath(value: string): string {
|
||||||
|
const trimmed = value.trim();
|
||||||
|
if (!trimmed) return trimmed;
|
||||||
|
if (/^[A-Za-z]:[\\/]/.test(trimmed) || trimmed.startsWith("\\\\")) return trimmed;
|
||||||
|
return path.resolve(trimmed);
|
||||||
|
}
|
||||||
@@ -144,7 +144,7 @@ async function boundedText(response: Response, limit: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function parseResult(value: Record<string, unknown>): LocalPoseResult {
|
function parseResult(value: Record<string, unknown>): LocalPoseResult {
|
||||||
if (value.mimeType !== "image/png" || !isPositiveInteger(value.width) || !isPositiveInteger(value.height) || typeof value.modelId !== "string" || value.device !== "cpu" || value.backend !== "openpose-body" || !isPositiveInteger(value.personCount) || typeof value.pngBase64 !== "string") {
|
if (value.mimeType !== "image/png" || !isPositiveInteger(value.width) || !isPositiveInteger(value.height) || typeof value.modelId !== "string" || value.device !== "cpu" || value.backend !== "openpose-body" || !isNonNegativeInteger(value.personCount) || typeof value.pngBase64 !== "string") {
|
||||||
throw new LocalRuntimeClientError("runtime_response_invalid", "本机姿态结果无效");
|
throw new LocalRuntimeClientError("runtime_response_invalid", "本机姿态结果无效");
|
||||||
}
|
}
|
||||||
const bytes = bytesFromBase64(value.pngBase64, "runtime_response_invalid", "本机姿态结果无效");
|
const bytes = bytesFromBase64(value.pngBase64, "runtime_response_invalid", "本机姿态结果无效");
|
||||||
@@ -207,3 +207,7 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|||||||
function isPositiveInteger(value: unknown): value is number {
|
function isPositiveInteger(value: unknown): value is number {
|
||||||
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isNonNegativeInteger(value: unknown): value is number {
|
||||||
|
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user