langchainjs Model Profiles Generator:基于 models.dev API 自动生成类型安全模型能力画像
【免费下载链接】langchainjsThe agent engineering platform项目地址: https://gitcode.com/GitHub_Trending/la/langchainjs
langchainjs 仓库在internal/model-profiles中内置了一个内部 CLI 工具,用于从 models.dev API 拉取模型能力数据、叠加 provider 级与模型级覆盖配置,并基于 TypeScript 编译器 AST 生成与@langchain/core的ModelProfile接口严格对齐的 TypeScript 文件。本文以该工具的官方文档 README 为主线,结合 cli.ts、config.ts、generator.ts 等源码,完整讲解其配置格式、字段映射、覆盖机制、AST 生成流程、路径安全校验与测试验证方式,读完后可直接掌握在一个 provider 包中接入并使用该生成器的全部要点。
工具定位与整体架构
模型能力画像(Model Profile)描述了每个模型的能力与约束:上下文窗口、最大输出 token、多模态输入输出、工具调用、结构化输出等。LangChain 的各个 provider 包(如langchain-openai)需要为每个模型维护一份这样的画像,用于运行时查询模型能力。手工维护这些数据既繁琐又容易滞后,而 Model Profiles Generator 将其自动化:
- 自动数据拉取:从 models.dev API(
https://models.dev/api.json)获取各 provider 最新模型数据; - Provider 级覆盖:对整个 provider 的所有模型统一施加配置修正;
- 模型级覆盖:对单个模型做细粒度修正,且优先级高于 provider 级;
- TypeScript AST 生成:使用 TypeScript Compiler API 构造代码节点,而非字符串拼接,保证生成结果类型安全;
- 格式化工具集成:自动按项目格式化配置输出代码(README 中描述为 Prettier 集成,而当前源码实现中实际调用的是
oxfmt,见下文“格式化与写盘”一节); - Monorepo 友好:基于
pnpm --filter在 pnpm workspace 中运行,并内置 monorepo 根目录定位与路径安全校验; - 类型安全:生成代码匹配
@langchain/core中的ModelProfile接口,接口定义见 profile.ts。
工具源码的组织结构如下(摘自 README 的 Architecture 一节,与实际目录一致):
internal/model-profiles/ ├── src/ │ ├── cli.ts # Command-line interface │ ├── config.ts # TOML config parsing and override logic │ ├── generator.ts # TypeScript code generation and API integration │ ├── api-schema.ts # TypeScript types for models.dev API │ └── tests/ # Test suite │ ├── config.test.ts │ └── generator.test.ts ├── package.json # Tool dependencies ├── vitest.config.ts # Test configuration └── README.md # This documentation该工具是一个内部私有包:package.json 中name为@langchain/model-profiles、private: true,不对外发布。其核心依赖包括@iarna/toml(TOML 解析)、commander(CLI 参数解析)、typescript(AST 生成)、zod(API schema 校验)以及 workspace 内的@langchain/core(仅用于ModelProfile类型引用)。
快速开始:TOML 配置与运行命令
创建配置文件
按 README 的 Basic Usage 一节,在某个 provider 包中创建 TOML 配置文件(例如profiles.toml),最小可用配置只有两项:
provider = "openai" output = "src/chat_models/profiles.ts"provider(必填):models.dev 中的 provider ID,例如openai。cli.ts 中会显式检查该字段,缺失时报错Provider name must be specified in the config file;output(必填):生成文件的输出路径,相对于配置文件所在目录解析(见 config.ts 中parseConfig对parsed.output以configDir为基准的解析逻辑)。
仓库中的真实配置可参考 langchain-openai 的 profiles.toml:
provider = "openai" output = "src/chat_models/profiles.ts" # Overrides applicable to all OpenAI models [overrides] imageUrlInputs = true pdfInputs = true pdfToolMessage = true imageToolMessage = true toolChoice = true structuredOutput = true # Model-specific overrides for gpt-3.5-turbo [overrides."gpt-3.5-turbo"] imageUrlInputs = false pdfInputs = false pdfToolMessage = false imageToolMessage = false structuredOutput = false运行生成器
按 README 的说明,通过 pnpm workspace 的--filter机制调用:
# From the model-profiles package pnpm --filter @langchain/model-profiles make --config profiles.toml # Or if running from within a provider package pnpm --filter @langchain/model-profiles make --config profiles.toml这里的make是 package.json 中定义的 npm script,其实际执行命令为tsx src/cli.ts,即直接用 tsx 运行 TypeScript 入口 cli.ts。CLI 使用 commander 定义了一个必填选项--config <path>(指向 TOML 配置文件),执行流程为:parseConfig解析配置 →separateOverrides拆分两级覆盖 →generateModelProfiles拉取数据并生成文件。任何异常都会打印Error: <message>并以退出码 1 结束(见 cli.ts)。
配置文件完整格式
README 中给出的完整 TOML 结构如下,overrides部分可同时包含 provider 级与模型级两类条目:
# Required: Provider ID from models.dev provider = "openai" # Required: Output path for generated TypeScript file (relative to config file) output = "src/chat_models/profiles.ts" # Optional: Provider-level overrides (applied to all models) [overrides] maxInputTokens = 100000 toolCalling = true structuredOutput = true imageUrlInputs = true # Optional: Model-specific overrides (override provider-level settings) [overrides."gpt-4"] maxOutputTokens = 8192 [overrides."gpt-3.5-turbo"] maxInputTokens = 16385 imageUrlInputs = false其中output是 TypeScript 接口中唯一必填的字段(provider在接口中标注为可选,但 CLI 会强制校验),overrides则是一个扁平的混合结构:顶层的 ModelProfile 字段名视为 provider 级覆盖,以模型名为 key 的嵌套表视为模型级覆盖。该设计在源码类型ConfigFile与OverridesConfig中有明确定义(见 config.ts)。
字段映射:models.dev API 数据如何变成 ModelProfile
api-schema.ts用 Zod 定义了 models.dev API 的Model/Provider/ProviderMap结构(文件头注释说明其改编自 SST models.dev 项目的 schema,许可证为 Apache-2.0),每个模型包含limit.context/limit.output(上下文与输出上限)、modalities.input/modalities.output(模态数组,枚举值为text、audio、image、video、pdf)、reasoning、tool_call、structured_output等字段(见 api-schema.ts)。
generator.ts中的modelToProfile函数完成了 API 模型到ModelProfile的字段映射(见 generator.ts):
| models.dev API 字段 | 生成的 ModelProfile 字段 | 说明 |
|---|---|---|
limit.context | maxInputTokens | 输入上下文窗口(token) |
limit.output | maxOutputTokens | 最大输出 token 数 |
modalities.input含image | imageInputs | 图片输入 |
modalities.input含audio | audioInputs | 音频输入 |
modalities.input含pdf | pdfInputs | PDF 输入 |
modalities.input含video | videoInputs | 视频输入 |
modalities.output含image | imageOutputs | 图片输出 |
modalities.output含audio | audioOutputs | 音频输出 |
modalities.output含video | videoOutputs | 视频输出 |
reasoning | reasoningOutput | 推理/思维链输出 |
tool_call | toolCalling | 工具调用 |
structured_output | structuredOutput | 结构化输出 |
需要特别注意:imageUrlInputs、imageToolMessage、pdfToolMessage、toolChoice这几个ModelProfile字段不会由 API 数据自动产生——modelToProfile中没有对应映射(models.dev schema 中不含这些维度的信息),它们只能来自 TOML 配置中的[overrides]。这也解释了为什么 langchain-openai 的 profiles.toml 把imageUrlInputs、toolChoice等写在 provider 级覆盖里。
ModelProfile接口的完整字段说明可参考 profile.ts:maxInputTokens表示输入上下文的总 token 预算(含 prompt、系统消息、对话历史),maxOutputTokens表示单次响应长度上限,imageUrlInputs表示可直接接受图片 URL 而非内嵌数据等,每个字段都附带 JSDoc 语义说明。
覆盖机制:provider 级 + 模型级两层合并
覆盖条目的分离
config.ts维护了一个白名单集合MODEL_PROFILE_FIELDS,包含 16 个合法字段名:maxInputTokens、imageInputs、imageUrlInputs、pdfInputs、audioInputs、videoInputs、imageToolMessage、pdfToolMessage、maxOutputTokens、reasoningOutput、imageOutputs、audioOutputs、videoOutputs、toolCalling、toolChoice、structuredOutput(见 config.ts)。separateOverrides据此区分两类条目(见 config.ts):
- key 命中白名单 → 归入
providerOverrides(provider 级); - key 不在白名单且 value 是非数组对象 → 归入
modelOverrides[modelName](模型级); - 其余条目(如白名单外、且不是嵌套对象的值)直接忽略。
config.test.ts中有对应单测:含invalidField: "should be ignored"的输入被验证不会进入providerOverrides(见 config.test.ts)。
合并优先级
applyOverrides的合并顺序是:基础画像(API 数据)→ provider 级覆盖 → 模型级覆盖,后者覆盖前者的同名字段(见 config.ts):
let result = { ...baseProfile }; if (providerOverrides) { result = { ...result, ...providerOverrides }; } // 模型级覆盖最后应用,优先级最高 if (modelOverrides) { result = { ...result, ...modelOverrides }; }测试用例覆盖了全部四种场景:仅 provider 覆盖、仅模型覆盖、两者叠加(各自生效)、以及模型覆盖与 provider 覆盖冲突时模型覆盖胜出(如maxInputTokens基础值 1000 → provider 覆盖 2000 → 模型覆盖 3000,最终 3000,见 config.test.ts)。generator.test.ts也在端到端层面验证了 provider 覆盖(toolCalling: true出现在生成文件中)与模型覆盖(maxOutputTokens: 8192出现在生成文件中)各自及同时生效的情形(见 generator.test.ts)。
代码生成实现:TypeScript AST 与格式化写盘
generateModelProfiles是核心入口(见 generator.ts),其流程为:
- 拉取数据:
fetchProviderData通过全局fetch请求https://models.dev/api.json,使用AbortSignal.timeout(30000)设置 30 秒超时;HTTP 非 2xx 时抛出Failed to fetch models.dev API: <statusText>; - 定位 provider:从返回的
ProviderMap中按 ID 取 provider,未找到时抛出Provider "<id>" not found in models.dev API(测试用例验证了这两个错误路径,见 generator.test.ts); - 逐模型合并:遍历
provider.models,对每个模型执行modelToProfile得到基础画像,再经applyOverrides合并两级覆盖; - AST 生成:
generateTypeScript使用 TypeScript Compiler API 构造源码节点(见 generator.ts)。生成的文件结构固定为三段:- 一条带 JSDoc 头注释的 type-only import:
import type { ModelProfile } from "@langchain/core/language_models/profile";,注释为 “This file was automatically generated by an automated script. Do not edit manually.”(该合成注释通过ts.addSyntheticLeadingComment添加); - 一个
const PROFILES: Record<string, ModelProfile> = { ... }声明,每个模型名作为字符串 key,画像对象中值为undefined的字段会被过滤掉不输出; export default PROFILES;。
- 一条带 JSDoc 头注释的 type-only import:
- 格式化与写盘:输出路径会再次经过 monorepo 路径校验(防御性检查,虽然
parseConfig已校验过一次),输出目录不存在时自动mkdir -p递归创建(generator.test.ts验证了嵌套目录自动创建,见 generator.test.ts)。
格式化细节
README 的功能列表中描述为 “Prettier Integration: Automatically formats generated code using your project's Prettier config”,但当前源码实现(generator.ts顶部import { format } from "oxfmt")实际调用的是oxfmt,并从 monorepo 根目录加载.oxfmtrc.jsonc/.oxfmtrc.json作为格式化配置(loadOxfmtConfig,见 generator.ts)。若格式化失败(含配置不存在),工具仅打印警告并降级写出未格式化的代码,不会中断生成流程。generator.test.ts中通过 mockoxfmt模块验证了格式化确实被调用(见 generator.test.ts)。
生成产物与消费方式
生成产物是各 provider 包中的src/chat_models/profiles.ts。以 langchain-openai 的 profiles.ts 为例,文件头部带有 “automatically generated… Do not edit manually.” 注释,随后是PROFILES常量表,每个模型条目完整列出 14 个能力字段:
import type { ModelProfile } from "@langchain/core/language_models/profile"; const PROFILES: Record<string, ModelProfile> = { "gpt-5-nano": { maxInputTokens: 400000, imageInputs: true, audioInputs: false, pdfInputs: true, videoInputs: false, maxOutputTokens: 128000, reasoningOutput: true, imageOutputs: false, audioOutputs: false, videoOutputs: false, toolCalling: true, structuredOutput: true, imageUrlInputs: true, pdfToolMessage: true, imageToolMessage: true, toolChoice: true, }, // ... 更多模型 }; export default PROFILES;消费端以 langchain-openai 的 base.ts 为例,通过import PROFILES from "./profiles.js"引入,并在get profile()访问器中按当前模型名查表:return PROFILES[this.model] ?? {}(见 base.ts)。因此生成器产出的静态画像表直接支撑了模型实例在运行时对自身能力的类型安全查询。仓库中langchain-google同样拥有生成产物 profiles.ts。
安全设计:monorepo 边界内的路径校验
由于工具会在文件系统中读写路径,config.ts实现了较完整的路径安全校验逻辑:
- monorepo 根定位(
findMonorepoRoot,见 config.ts):以process.cwd()作为可信起点向上逐级查找,优先识别pnpm-workspace.yaml;备选指标是含"private": true的package.json且同级存在turbo.json; - INIT_CWD 处理:通过
pnpm --filter运行时,pnpm 会把命令发起的原始工作目录写入环境变量INIT_CWD。parseConfig以INIT_CWD(或回退cwd())作为解析--config相对路径的基准,但会先验证该目录位于 monorepo 根之内,否则抛出路径越界错误(见 config.ts); - 双重校验输出路径:
output在parseConfig中相对配置文件目录解析并校验一次,在generateModelProfiles写盘前再防御性校验一次(validatePathInMonorepo,见 config.ts)。越界路径会抛出形如Path "..." resolves to "..." which is outside the monorepo root "..."的错误; - 测试同样遵守该约束:
generator.test.ts的临时目录特意创建在 monorepo 根下的.test-temp中以满足路径校验(见 generator.test.ts)。
测试验证
工具的测试基于 vitest(pnpm --filter @langchain/model-profiles test对应 package.json 中的test: vitest run),覆盖三个层面:
- config.test.ts:单测
separateOverrides的白名单过滤、空/未定义输入、以及applyOverrides的四层合并优先级; - generator.test.ts:mock 全局
fetch与oxfmt模块,端到端验证生成文件包含import type { ModelProfile }、const PROFILES、具体模型 key 与export default PROFILES,并覆盖 API 失败、provider 不存在等错误分支。
使用小结
- 为某个 provider 生成画像只需两步:在 provider 包内写好含
provider/output/ 可选[overrides]的 TOML,然后执行pnpm --filter @langchain/model-profiles make --config profiles.toml; output相对配置文件目录解析,且所有路径(config 基准目录、配置文件、输出文件)必须落在 monorepo 根之内;- 字段白名单之外的覆盖项会被静默忽略,模型级覆盖始终优先于 provider 级覆盖,二者都优先于 API 原始数据;
imageUrlInputs、imageToolMessage、pdfToolMessage、toolChoice四类字段无法从 API 自动获得,只能依赖[overrides]补齐;- 生成文件带 “Do not edit manually.” 头注释,属于再生成产物,人工修改会在下次生成时丢失;
- 运行前提:位于 langchainjs 的 pnpm workspace 环境中执行(工具依赖 monorepo 根定位),且需要网络可达 models.dev API(30 秒超时)。
【免费下载链接】langchainjsThe agent engineering platform项目地址: https://gitcode.com/GitHub_Trending/la/langchainjs
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考