diff --git a/.changeset/sour-rules-beg.md b/.changeset/sour-rules-beg.md new file mode 100644 index 00000000..c5634aed --- /dev/null +++ b/.changeset/sour-rules-beg.md @@ -0,0 +1,8 @@ +--- +"@plutolang/pyright-deducer": patch +"@plutolang/cli": patch +--- + +feat(deducer): configure local arch for pluto run on Mac + +Avoid unnecessary use of Docker for x86 pypi package downloads when executing pluto run on Mac. Previously, target architecture was set to x86 for all environments, leading to Docker usage on Mac. This change sets the target architecture to the local one during pluto run execution. diff --git a/apps/cli/src/commands/run.ts b/apps/cli/src/commands/run.ts index debc728a..4509fb99 100644 --- a/apps/cli/src/commands/run.ts +++ b/apps/cli/src/commands/run.ts @@ -10,6 +10,8 @@ import { loadAndDeduce } from "./compile"; import { deployWithAdapter } from "./deploy"; import { buildAdapterByProvisionType, + getCurrentArch, + getCurrentPlatform, getDefaultDeducerPkg, getDefaultEntrypoint, loadProjectAndStack, @@ -27,6 +29,8 @@ export async function run(entrypoint: string, options: RunOptions) { const projectRoot = loadProjectRoot(); const { project } = loadProjectAndStack(projectRoot); const stack = new config.Stack("local_run", PlatformType.Simulator, ProvisionType.Simulator); + stack.configs["targetArch"] = getCurrentArch(); + stack.configs["targetPlatform"] = getCurrentPlatform(); // Load the environment variables from the `.env` files. loadDotEnvs(projectRoot, stack.name, false); diff --git a/apps/cli/src/commands/utils.ts b/apps/cli/src/commands/utils.ts index c29b3d73..dd4c496d 100644 --- a/apps/cli/src/commands/utils.ts +++ b/apps/cli/src/commands/utils.ts @@ -6,6 +6,23 @@ import { Architecture } from "@plutolang/base/arch"; import { ExitError } from "../errors"; import { isPlutoProject, loadProject } from "../utils"; +/** + * Get the architecture of the current system. + * @returns The architecture of the current system. + */ +export function getCurrentArch() { + const currentArch = process.arch === "x64" ? "x86_64" : process.arch; + return currentArch; +} + +/** + * Get the platform of the current system. + * @returns The platform of the current system. + */ +export function getCurrentPlatform() { + return process.platform; +} + /** * load the default export of the target package. */ diff --git a/components/deducers/python-pyright/src/common/os-utils.ts b/components/deducers/python-pyright/src/common/os-utils.ts new file mode 100644 index 00000000..2a2d0691 --- /dev/null +++ b/components/deducers/python-pyright/src/common/os-utils.ts @@ -0,0 +1,16 @@ +/** + * Get the architecture of the current system. + * @returns The architecture of the current system. + */ +export function getCurrentArch() { + const currentArch = process.arch === "x64" ? "x86_64" : process.arch; + return currentArch; +} + +/** + * Get the platform of the current system. + * @returns The platform of the current system. + */ +export function getCurrentPlatform() { + return process.platform; +} diff --git a/components/deducers/python-pyright/src/index.ts b/components/deducers/python-pyright/src/index.ts index e84605a9..d8610d57 100644 --- a/components/deducers/python-pyright/src/index.ts +++ b/components/deducers/python-pyright/src/index.ts @@ -301,6 +301,20 @@ export default class PyrightDeducer extends core.Deducer { const installPkg = this.stack.configs["bundleWithDependencies"] !== false; const runtime = await getDefaultPythonRuntime(); + const targetArch = this.stack.configs["targetArch"] ?? "x86_64"; + if (targetArch !== "x86_64" && this.stack.name !== "local_run") { + throw new Error( + `The non-x86_64 architecture is only supported when the 'pluto run' command is executed locally. The current stack is ${this.stack.name}.` + ); + } + + const targetPlatform = this.stack.configs["targetPlatform"] ?? "linux"; + if (targetPlatform !== "linux" && this.stack.name !== "local_run") { + throw new Error( + `The non-linux platform is only supported iwhen the 'pluto run' command is executed locally. The current stack is ${this.stack.name}.` + ); + } + await Promise.all( closures.map((closure) => bundleOne(closure, this.stack.platformType, this.importFinder!, this.bundleFilename) @@ -331,7 +345,7 @@ export default class PyrightDeducer extends core.Deducer { // multiple places, including the Deducer and the infrastructure SDK. The former determines // the Python version and architecture for bundling dependencies, while the latter sets the // cloud runtime environment. - await bundleModules(runtime, "x86_64", modules, closure.path, destBaseDir, { + await bundleModules(runtime, targetPlatform, targetArch, modules, closure.path, destBaseDir, { install: installPkg, slim: true, // By default, we'll delete the `dist-info` directory, but LangChain needs it, so we'll just diff --git a/components/deducers/python-pyright/src/module-bundler/bundle-module.ts b/components/deducers/python-pyright/src/module-bundler/bundle-module.ts index 62f61416..061e4ef2 100644 --- a/components/deducers/python-pyright/src/module-bundler/bundle-module.ts +++ b/components/deducers/python-pyright/src/module-bundler/bundle-module.ts @@ -7,6 +7,7 @@ import * as AwsUtils from "./aws-utils"; import * as CmdUtils from "./command-utils"; import * as MetadataUtils from "./metadata"; import { getIndexUrls, IndexUrl } from "./index-url"; +import { getCurrentArch, getCurrentPlatform } from "../common/os-utils"; import { Architecture, InstalledModule, Module, ModuleType, Runtime } from "./types"; export interface BundleModulesOptions { @@ -23,21 +24,30 @@ export interface BundleModulesOptions { export async function bundleModules( runtime: Runtime, + platform: typeof process.platform, architecture: Architecture, modules: readonly Module[], bundleDir: string, sitePackagesDir: string, options: BundleModulesOptions = {} ): Promise { - // When running on non-Linux platforms or packaging for cross-architecture, the Docker is - // required. If the user has explicitly disabled Docker, throw an error. - const currentArch = process.arch === "x64" ? "x86_64" : process.arch; - if (process.platform !== "linux" || currentArch !== architecture) { - if (options.dockerPip === false) { + if (getCurrentPlatform() !== platform || getCurrentArch() !== architecture) { + // In this case, the user is trying to bundle the modules for a different platform or + // architecture. We need to check if the Docker can meet the requirement. + + if (platform !== "linux") { + throw new Error("Only Linux is supported for cross-platfrom."); + } + + if (!Architecture.isSupported(architecture)) { throw new Error( - "Docker is required to bundle modules on non-Linux platforms, or for cross-architecture." + `The architecture '${architecture}' is not supported for cross-architecture.` ); } + + if (options.dockerPip === false) { + throw new Error("Docker is required to bundle modules for cross-architecture."); + } options.dockerPip = true; } diff --git a/components/deducers/python-pyright/src/module-bundler/types.ts b/components/deducers/python-pyright/src/module-bundler/types.ts index 769248fe..d34c1f4c 100644 --- a/components/deducers/python-pyright/src/module-bundler/types.ts +++ b/components/deducers/python-pyright/src/module-bundler/types.ts @@ -1,6 +1,12 @@ export type Runtime = "python3.12" | "python3.11" | "python3.10" | "python3.9" | "python3.8"; export type Architecture = "x86_64" | "arm64"; +export namespace Architecture { + export function isSupported(arch: Architecture): boolean { + return arch === "x86_64" || arch === "arm64"; + } +} + export enum ModuleType { Local = "local", Installed = "installed", diff --git a/components/deducers/python-pyright/src/test/module-bundler/bundle-module.test.ts b/components/deducers/python-pyright/src/test/module-bundler/bundle-module.test.ts index 2692e9db..565d575a 100644 --- a/components/deducers/python-pyright/src/test/module-bundler/bundle-module.test.ts +++ b/components/deducers/python-pyright/src/test/module-bundler/bundle-module.test.ts @@ -5,12 +5,15 @@ import { bundleModules } from "../../module-bundler/bundle-module"; import * as CommandUtils from "../../module-bundler/command-utils"; describe("bundle with the local modules", () => { + const platform = "linux"; + test("should correctly bundle with a local module", async () => { const { tmpdir, cleanup } = getTmpDir(); await fs.writeFile(`${tmpdir}/module.py`, "def hello():\n return 'Hello, world!'\n"); const runtime = await CommandUtils.getDefaultPythonRuntime(); + const architecture = "x86_64"; const modules: Module[] = [LocalModule.create("module", `${tmpdir}/module.py`)]; @@ -22,7 +25,7 @@ describe("bundle with the local modules", () => { try { await expect( - bundleModules(runtime, architecture, modules, bundleDir, sitePackagesDir, options) + bundleModules(runtime, platform, architecture, modules, bundleDir, sitePackagesDir, options) ).resolves.not.toThrow(); const files = await fs.readdir(bundleDir); @@ -50,7 +53,7 @@ describe("bundle with the local modules", () => { try { await expect( - bundleModules(runtime, architecture, modules, bundleDir, sitePackagesDir, options) + bundleModules(runtime, platform, architecture, modules, bundleDir, sitePackagesDir, options) ).resolves.not.toThrow(); const files = await fs.readdir(bundleDir); @@ -62,6 +65,8 @@ describe("bundle with the local modules", () => { }); describe("bundle with the packages that need to install", () => { + const platform = "linux"; + test("should bundle packages and remove useless files", async () => { const { tmpdir, cleanup } = getTmpDir(); @@ -76,7 +81,15 @@ describe("bundle with the packages that need to install", () => { const options = { slim: true }; try { - await bundleModules(runtime, architecture, modules, targetFolder, targetFolder, options); + await bundleModules( + runtime, + platform, + architecture, + modules, + targetFolder, + targetFolder, + options + ); const files = fs.readdirSync(targetFolder); expect(files).toContain("requirements.txt"); @@ -110,7 +123,7 @@ describe("bundle with the packages that need to install", () => { try { await expect( - bundleModules(runtime, architecture, modules, targetFolder, targetFolder, options) + bundleModules(runtime, platform, architecture, modules, targetFolder, targetFolder, options) ).rejects.toThrow( "Docker is required to bundle modules on non-Linux platforms, or for cross-architecture." ); @@ -153,7 +166,7 @@ describe("bundle with the packages that need to install", () => { try { await expect( - bundleModules(runtime, architecture, modules, targetFolder, targetFolder, options) + bundleModules(runtime, platform, architecture, modules, targetFolder, targetFolder, options) ).rejects.toThrow( `${runtime} is not installed. Please install it first, or use Docker to bundle modules instead.` ); diff --git a/examples/rag-qa-bot-with-web/README.md b/examples/rag-qa-bot-with-web/README.md index 89e30be8..fa8ef865 100644 --- a/examples/rag-qa-bot-with-web/README.md +++ b/examples/rag-qa-bot-with-web/README.md @@ -9,8 +9,6 @@ deployUrl: https://codesandbox.io/p/devbox/pluto-doc-qa-bot-forked-8njh42 Let me introduce you to an incredibly simple way to build a document Q&A bot, which allows you to create a personalized Web Q&A bot based on your GitHub documentation repository in **just 5 minutes**. -First, let's take a look at the result. You can also experience it by opening [this link](https://xw3vdvjmyp7jig7tmrvrqbisiu0peosf.lambda-url.us-east-1.on.aws/). -

Demo

diff --git a/examples/rag-qa-bot-with-web/README_zh.md b/examples/rag-qa-bot-with-web/README_zh.md index 91663d2b..439ae9cd 100644 --- a/examples/rag-qa-bot-with-web/README_zh.md +++ b/examples/rag-qa-bot-with-web/README_zh.md @@ -9,8 +9,6 @@ deployUrl: https://codesandbox.io/p/devbox/pluto-doc-qa-bot-forked-8njh42 这里给大家介绍一种非常简单的构建文档问答机器人的方式,**只需要 5 分钟**就可以基于你的 GitHub 文档仓库创建一个专属的问答机器人,并且将其部署到 AWS 上(免费),让你的用户可以通过 Web 界面来提问。 -首先看一下效果,你也可以打开[这个链接](https://xw3vdvjmyp7jig7tmrvrqbisiu0peosf.lambda-url.us-east-1.on.aws/)来体验。 -

Demo