我写过一些依赖二进制资源(例如原生共享库)以及其他有大资源(例如llm权重)的库。
原则上,这两种类型的依赖都可以通过deps工具满足而无需任何更改,因为deps cli可以获取资源并将其添加到类路径。
但是,还有一些额外的挑战,使得在使用deps cli处理大型和/或二进制资源时感到有些不便。
大型资源很大
我避免使用deps工具处理大型资源,因为有几个缺失的功能
a) 下载依赖项之前没有是/否提示。
当调用cli时,下载可能100多兆的依赖项似乎是合理的,但直接下载千兆级别数据依赖项而事先没有询问则感觉不太合适。
b) 下载依赖项时没有进度指示器
下载依赖项通常很快,但在下载几GB依赖项时有一个进度指示器会更好。
原生依赖项通常很大
原生依赖项的大小可能从几MB到1GB不等(例如Chromium嵌入式框架)。
原生依赖项依赖于平台/操作系统
我认为有一些maven魔法可以帮助解决这个问题,但我还没有机会看看。不管怎样,运行程序所需的工件依赖于程序运行的平台。通常,包括所有可能的平台的工件是可行的,但这可能是不必要地使用资源,并且可能在某些情况下不起作用。
在加载之前必须从jar中提取共享库
许多ffi库(如JNA)会为你做这件事。它根据ffi库以非正式的方式进行实现。它可能或可能不起作用,具体取决于共享库是独立库还是依赖于其他共享库。
可能的,deps工具可以使用一种可接受、统一的方法提取共享库。
预先编译的Linux共享库几乎不起作用
你可以预先为mac osx和windows编译共享库,以获得广泛的兼容性且“即用即行”。在Linux上,有很多需要注意的地方。一般来说,如果你有一个独立的共享库并且用它进行了编译(例如,使用zig [llmdb]),则可以得到基本可以工作的东西,但如果有一个依赖多个不使用zig的共享库(例如,graphviz),则兼容性将大幅度下降。
原生依赖项应该从源代码编译?
一些包管理器提供预编译的二进制文件(例如conda),但似乎许多包管理器倾向于从源代码编译(例如,pip、macports、homebrew等)。
从源代码编译可能有助于以下问题:
- 从源代码编译避开了在Linux上提供本地依赖项的许多挑战
- 本地依赖项可能是Git依赖项!
- 有些开发者非常不喜欢预编译的二进制文件,并强烈倾向于从源代码编译。
明显,支持从源代码编译有其自身的挑战。
上述内容涵盖了关于大型和/或二进制资源的问题。以下是几个具体案例:
- clj-cef:包装了 chromium 内嵌框架。框架本身大约为 0.5-1.5GB,取决于平台,包括共享库和大型资源文件。还需要额外的小型共享库。
- clj-graphviz:包装了 graphviz 的C库。比较棘手,因为存在多个共享库,每个共享库都有额外的依赖(例如,libpng)。
- llama.clj:包装了 llama.cpp 库,用于本地运行 llm。还希望添加特定的 llm 重量作为依赖项,但它们的体积可以非常大(例如,几个吉字节到数百吉字节)。此外,llama.cpp 可以编译为支持gpu,但我还没有想出一种在没有从源代码编译选项的情况下便携的方式。
我知道这和现有工具有很多重叠,所以也许可以以某种方式让依赖项 cli 集成到现有工具中
- 获取原生依赖项:pip,conda,homebrew,macports,apt,scoop 等。
- 获取数据依赖项:hugging face,https://dvc.org/
无论如何,我已经多次尝试找到包含本地和/或大型依赖项的良好方法,所有这些选项仍然感觉相当不自然。