Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(deducer): configure local arch for pluto run on Mac #337

Merged
merged 1 commit into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/sour-rules-beg.md
Original file line number Diff line number Diff line change
@@ -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.
4 changes: 4 additions & 0 deletions apps/cli/src/commands/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { loadAndDeduce } from "./compile";
import { deployWithAdapter } from "./deploy";
import {
buildAdapterByProvisionType,
getCurrentArch,
getCurrentPlatform,
getDefaultDeducerPkg,
getDefaultEntrypoint,
loadProjectAndStack,
Expand All @@ -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);
Expand Down
17 changes: 17 additions & 0 deletions apps/cli/src/commands/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
16 changes: 16 additions & 0 deletions components/deducers/python-pyright/src/common/os-utils.ts
Original file line number Diff line number Diff line change
@@ -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;
}
16 changes: 15 additions & 1 deletion components/deducers/python-pyright/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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<void> {
// 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;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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`)];

Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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();

Expand All @@ -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");
Expand Down Expand Up @@ -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."
);
Expand Down Expand Up @@ -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.`
);
Expand Down
2 changes: 0 additions & 2 deletions examples/rag-qa-bot-with-web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/).

<p align="center">
<img src="./assets/doc-qa-bot-web-show-en.png" alt="Demo" width="500" />
</p>
Expand Down
2 changes: 0 additions & 2 deletions examples/rag-qa-bot-with-web/README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/)来体验。

<p align="center">
<img src="./assets/doc-qa-bot-web-show.png" alt="Demo" width="500" />
</p>
Expand Down
Loading