labring / FastGPT

FastGPT is a knowledge-based platform built on the LLMs, offers a comprehensive suite of out-of-the-box capabilities such as data processing, RAG retrieval, and visual AI workflow orchestration, letting you easily develop and deploy complex question-answering systems without the need for extensive setup or configuration.
https://tryfastgpt.ai
Other
17.49k stars 4.69k forks source link

错误配置serverComponentsExternalPackages和optimizePackageImports导致升级nextjs 14之后启动失败 #1568

Closed RandyZ closed 5 months ago

RandyZ commented 5 months ago

例行检查

你的版本

问题描述, 日志截图 我是基于4.6.9做的定制性开发,由于后续想接入我们自己的模块项目,所以需要升级nextjs版本,从13升级到了14。改用了turbopack来作为打包工具。

近期准备更新一下FastGPT的代码到4.8.x,新的节点调试和节点之间交互是我们需要的。所以想做一下升级。

“Error: The packages specified in the 'transpilePackages' conflict with the 'serverComponentsExternalPackages': mongoose, pg”

复现步骤

  1. 在4.6.9版本上升级到Nextjs 14
  2. 合并4.8.0版本代码

原因和分析 产生的原因是在v4.8-preview的这个tag中增加了一段配置,导致用nextjs 14运行出现异常。

transpilePackages: ['@fastgpt/*', 'ahooks', '@chakra-ui/*', 'react'],
experimental: {
    // 外部包独立打包
    serverComponentsExternalPackages: ['mongoose', 'pg'],
    // 指定导出包优化,按需引入包模块
    optimizePackageImports: ['mongoose', 'pg'],
  }

我对于Nextjs中这几个选项的了解是这样的:

所以从意图上说希望'mongoose', 'pg'独立打包并且优化加载方式为require是没问题的,但是详细看了下13和14的实现细节:

在13版本中的作用是这样的

从下面的代码可以看出serverComponentsExternalPackages的优先级会高于transpilePackages生效,优先隔离掉服务端运行的包。不再做打包优化和兼容性处理

  1. 
    const resolveConfig = {
    ......
    alias: {
    ......
    // For Node server, we need to re-alias the package imports to prefer to
    // resolve to the ESM export.
    ...isNodeServer ? getBarrelOptimizationAliases(config.experimental.optimizePackageImports || []) : {},
    }
    ......
    }

// Alias these modules to be resolved with "module" if possible. function getBarrelOptimizationAliases(packages) { const aliases = {}; const mainFields = [ "module", "main" ]; for (const pkg of packages){ try { const descriptionFileData = require(${pkg}/package.json); const descriptionFilePath = require.resolve(${pkg}/package.json); for (const field of mainFields){ if (descriptionFileData.hasOwnProperty(field)) { aliases[pkg + "$"] = _path.default.join(_path.default.dirname(descriptionFilePath), descriptionFileData[field]); break; } } } catch {} } return aliases; } ........... // If a package is included in transpilePackages, we don't want to make it external. // And also, if that resource is an ES module, we bundle it too because we can't // rely on the require hook to alias react to our precompiled version. const shouldBeBundled = isResourceInPackages(res, config.transpilePackages, resolvedExternalPackageDirs) || isEsm && isAppLayer; if (/node_modules[/\].*.[mc]?js$/.test(res)) { if ((0, _utils.isWebpackServerLayer)(layer)) { // All packages should be bundled for the server layer if they're not opted out. // This option takes priority over the transpilePackages option. if (optOutBundlingPackageRegex.test(res)) { return ${externalType} ${request}; } return; } if (shouldBeBundled) return; // Anything else that is standard JavaScript within node_modules // can be externalized. return ${externalType} ${request}; }

**可以看出这个配置只是配置了一下别名,同时如果可能的话别名都使用ESM的export,借助ESM按需加载**

### 在14版本中的作用是这样的
14版本中实现虽然有所变化,但是整个的实现原理进行了调整optimizePackageImports直接被融合到了transpilePackages,也就是说transpilePackages选项不止处理兼容ESM导入,还会做导入优化。
而从`The packages specified in the 'transpilePackages' conflict with the 'serverComponentsExternalPackages': ${externalPackageConflicts.join(", ")}`这个异常上也能看出,Nextjs的本意是serverComponentsExternalPackages的库是不需要兼容和优化的,直接在服务段运行即可。

// since pages doesn't always bundle by default we need to // auto-include optimizePackageImports in transpilePackages const finalTranspilePackages = config.transpilePackages || []; for (const pkg of config.experimental.optimizePackageImports || []){ if (!finalTranspilePackages.includes(pkg)) { finalTranspilePackages.push(pkg); } }

// The serverComponentsExternalPackages should not conflict with // the transpilePackages. if (config.experimental.serverComponentsExternalPackages && finalTranspilePackages) { const externalPackageConflicts = finalTranspilePackages.filter((pkg)=>{ var _config_experimental_serverComponentsExternalPackages; return (_config_experimental_serverComponentsExternalPackages = config.experimental.serverComponentsExternalPackages) == null ? void 0 : _config_experimental_serverComponentsExternalPackages.includes(pkg); }); if (externalPackageConflicts.length > 0) { throw new Error(The packages specified in the 'transpilePackages' conflict with the 'serverComponentsExternalPackages': ${externalPackageConflicts.join(", ")}); } } // 后面会创建handleExternals来处理按需导入

**预期结果**
综合13和14的Nextjs变化我建议按照如下进行配置

transpilePackages: ['@fastgpt/', 'ahooks', '@chakra-ui/', 'react'], experimental: { // 外部包独立打包 serverComponentsExternalPackages: ['mongoose', 'pg'], }


随后提交一个我测试过的MR,可以考虑升级到14, 13版本我们测试的时候发现过一些Worker异常内存占用的问题。
**相关截图**
RandyZ commented 5 months ago

https://github.com/labring/FastGPT/pull/1569

c121914yu commented 5 months ago

感谢。 在 4.8.1 的代码中(目前 main 已经合并),nextjs 已经升级到 14 了。可以合并下。

c121914yu commented 5 months ago

worker 异常问题主要来自于 tiktoken 包。目前已经用 wasm 替代,观测了一段时间,内存正常了。

c121914yu commented 5 months ago

你的修改是正确的。目前配置应该改成 serverComponentsExternalPackages 。麻烦从 main 分支进行 rebase 和修改。

RandyZ commented 5 months ago

你的修改是正确的。目前配置应该改成 serverComponentsExternalPackages 。麻烦从 main 分支进行 rebase 和修改。

好的,我会基于main再提交一个pr

RandyZ commented 5 months ago

https://github.com/labring/FastGPT/pull/1570 更新了一下pr