ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Jan Assistant 扩展开发指南:用 TypeScript 构建、打包并测试你自己的 Jan 扩展

Jan Assistant 扩展开发指南:用 TypeScript 构建、打包并测试你自己的 Jan 扩展 Jan Assistant 扩展开发指南用 TypeScript 构建、打包并测试你自己的 Jan 扩展【免费下载链接】janJan is an open source alternative to ChatGPT that runs 100% offline on your computer.项目地址: https://gitcode.com/GitHub_Trending/ja/jan本文以 Jan 仓库中的 assistant-extension 模板 为核心结合 core 包中的扩展基类、扩展源码 与 单元测试完整讲解如何用 TypeScript 创建、打包、安装和演进一个 Jan 扩展从package.json元数据定义、rolldown 构建产物到file://虚拟文件系统中的助手数据迁移机制读者可据此独立开发自己的 Jan 扩展。一、模板定位assistant-extension 在 Jan 仓库中的角色Jan 是运行在本地的开源 AI 聊天应用其功能通过“扩展Extension”机制进行模块化拆分助手管理、对话编排、推理后端、模型下载、RAG、向量库等能力都实现为独立扩展由janhq/core包提供统一的事件、文件系统和类型系统。extensions/assistant-extension 目录既是 Jan 内置的默认 AI 助手实现也被官方 README 明确定位为一个可直接 fork 使用的扩展脚手架模板。README 开篇即说明Use this template to bootstrap the creation of a TypeScript Jan extension.因此围绕该目录学习既能掌握“如何写一个新扩展”又能顺带读懂 Jan 默认助手默认系统提示词、默认采样参数、助手持久化与数据迁移的完整实现。二、创建你自己的扩展模板使用与初始环境搭建README 给出了标准的模板使用流程点击仓库顶部的 Use this template 按钮选择 Create a new repository为新的仓库选择 owner 与名称点击 Create repository克隆你的新仓库到本地。环境要求模板 README 明确要求一个较新的 Node.js 环境20.x 或更高版本如果在使用nodenv/nvm这类版本管理器可以在仓库根目录按package.json中指定的版本安装对应 Node。需要说明的一个仓库事实是当前 package.json 中声明了packageManager: yarn4.5.3且依赖使用了workspace:*协议janhq/core: workspace:*这意味着在 Jan monorepo 内部开发时应使用 Yarn 4 工作区而在 fork 出的独立模板仓库中按 README 使用npm install同样可行独立仓库中janhq/core会解析为 npm 发布版本。依赖安装、打包与产物检查README 描述的三步工作流以及当前仓库中对应的真实脚本# 1. 安装依赖 npm install # 2. 打包 TypeScriptREADME 中的命令当前仓库脚本名为 build npm run bundle # 对应当前 package.json 中的 build: rolldown -c rolldown.config.mjs # 3. 检查产物扩展目录中会出现 .tgz 文件对照当前 package.json 的scripts可确认模板命令与仓库实际脚本的对应关系{ build: rolldown -c rolldown.config.mjs, build:publish: rimraf *.tgz --glob || true yarn build npm pack cpx *.tgz ../../pre-install, test: vitest run }即build完成 rolldown 打包build:publish在清理旧产物后执行build再用npm pack生成.tgz安装包并复制到仓库的pre-install目录随 Jan 应用预装test运行 vitest 测试。README 中提到的 “tgz 产物”即由npm pack产生。三、扩展元数据package.json 字段逐项解读README 指出package.json定义了扩展的名称、主入口、描述和版本等元数据fork 模板后必须更新其中的name与description。以当前模板文件为参照各关键字段含义如下字段当前值作用namejanhq/assistant-extension扩展包名是 Jan 识别扩展的唯一标识productNameJan Assistant展示在产品界面中的名称version1.0.2扩展版本号maindist/index.js扩展主入口打包产物路径nodedist/node/index.js节点侧入口如存在独立后端逻辑author/licenseJan servicejan.ai/AGPL-3.0作者与协议信息dependenciesjanhq/core唯一运行时依赖Jan 扩展核心包filesdist/*,package.json,README.md发布进.tgz包的文件白名单installConfig.hoistingLimitsworkspaces在 monorepo 中避免依赖被提升hoisting到工作区外这些字段并非摆设rolldown 构建配置会直接读取它们见下一节core 包的BaseExtension构造参数name / productName / url / active / description / version见 extension.ts也与元数据一一对应。四、构建管线从 src/index.ts 到 dist/index.jsrolldown.config.mjs 完整展示了扩展的打包逻辑值得逐行理解import { defineConfig } from rolldown import pkgJson from ./package.json with { type: json } export default defineConfig([ { input: src/index.ts, output: { format: esm, file: dist/index.js, // 即 package.json 中的 main 字段 }, platform: browser, define: { NODE: JSON.stringify(${pkgJson.name}/${pkgJson.node}), VERSION: JSON.stringify(pkgJson.version), }, } ])要点入口是src/index.ts输出为ESM 格式单文件dist/index.js正好落在package.json的main声明位置platform: browser表明扩展代码运行在浏览器/前端运行时环境中define在编译期注入了两个全局常量NODE与VERSION其值来自package.json的name/node/version字段。这与 src/types/global.d.ts 中的声明相呼应declare const NODE: string declare const VERSION: string也就是说扩展代码在运行时可以直接读取自身的包名与版本号无需额外配置。tsconfig.json 则规定了编译口径target: es2016、module: ES6、declaration: true声明文件输出到dist/types、sourceMap: true与 ESM 打包目标保持一致。五、扩展代码骨架继承 AssistantExtension 与生命周期模板 README 对扩展代码有两点核心提示大部分 Jan 扩展函数都是异步处理的扩展函数会返回Promiseany事件订阅的典型写法如下摘自 READMEimport { events, MessageEvent, MessageRequest } from janhq/core function onStart(): Promiseany { return events.on(MessageEvent.OnMessageSent, (data: MessageRequest) this.inference(data) ) }在 core 包中扩展体系以抽象类层次组织BaseExtension所有扩展的基类定义了name、url、active、description、version等属性以及两个必须实现的生命周期钩子onLoad()/onUnload()还提供了registerModels、registerSettings等通用能力AssistantExtension助手类型扩展的抽象中间层声明type()返回ExtensionTypeEnum.Assistant并要求实现三个抽象方法export abstract class AssistantExtension extends BaseExtension implements AssistantInterface { type(): ExtensionTypeEnum | undefined { return ExtensionTypeEnum.Assistant } abstract createAssistant(assistant: Assistant): Promisevoid abstract deleteAssistant(assistant: Assistant): Promisevoid abstract getAssistants(): PromiseAssistant[] }Assistant的数据形状定义在 core/src/types/assistant/assistantEntity.ts包含avatar、id、object、created_at、name、description、model、instructions、tools、file_ids、metadata等字段并配有逐字段注释是编写助手相关扩展时最核心的类型契约。模板 README 指向的 Jan Extension Core 模块文档在本仓库中即 core/README.md。六、默认助手实现解析onLoad、持久化与种子数据extensions/assistant-extension/src/index.ts 中的JanAssistantExtension是模板的参考实现onLoad()L17-L39展示了扩展加载时应当完成的标准初始化序列async onLoad() { if (!(await fs.existsSync(file://assistants))) { await fs.mkdir(file://assistants) } // Run migrations if needed await this.runMigrations() const assistants await this.readAssistantsFromDisk() if (assistants.length 0) { const assistantWithParams { ...this.defaultAssistant, parameters: { temperature: 0.7, top_k: 20, top_p: 0.8, repeat_penalty: 1.12, }, } await this.createAssistant(assistantWithParams as Assistant) } }这里体现了 Jan 扩展编程模型的三个关键特征虚拟文件系统一切持久化都通过file://前缀路径进行如file://assistants、file://assistants/id/assistant.json由janhq/core导出的fs模块统一抽象屏蔽了不同平台的真实磁盘差异。这是一个写自定义扩展时必须记住的约定——不要直接使用 Node 的fs模块。幂等初始化先确保目录存在再执行迁移最后仅在磁盘为空时写入种子数据避免覆盖用户已自定义的助手对应测试用例 “does not overwrite an existing persisted assistant on load”。种子参数默认助手Janid: jan、avatar: 、model: *表示适配所有已安装模型附带默认采样参数temperature: 0.7 / top_k: 20 / top_p: 0.8 / repeat_penalty: 1.12其instructions是一段要求“按用户语言回复、逐步推理、作为专业工具调用者分析信息缺口”的系统提示词并带有{{current_date}}日期占位符tools中默认挂了一个禁用状态的retrieval工具附带top_k: 2、chunk_size: 1024、chunk_overlap: 64的 RAG 检索配置与检索提示词模板L333-L351。CRUD 方法本身也非常短小是“最小可运行扩展”的范例createAssistantL281-L292确保file://assistants/id/目录存在后把助手序列化为缩进 JSON 写入assistant.jsondeleteAssistantL294-L303存在即删除assistant.json不存在则为空操作no-opgetAssistantsL275-L279优先读取磁盘数据磁盘为空时回退到内置的defaultAssistant保证上层调用总能拿到至少一个可用助手。私有方法readAssistantsFromDisk还会跳过缺少assistant.json的目录以及 JSON 解析失败的损坏文件只记录错误而不中断整体加载。七、数据迁移机制版本化 .migration_version 与三级迁移JanAssistantExtension内置了一套值得借鉴的轻量数据迁移方案L41-L95迁移版本记录在file://assistants/.migration_version文件中当前版本常量CURRENT_MIGRATION_VERSION 3getCurrentMigrationVersion()读取该文件文件缺失或内容无法解析parseInt得到NaN时一律按版本 0处理从而保证迁移一定会补跑runMigrations()按currentVersion N的条件逐档执行迁移每完成一档立即写回版本号版本迁移内容v1将旧版指令前缀You are a helpful AI assistant.改写为You are Jan, a helpful AI assistant.并保留后续自定义内容用startsWith 字符串截取实现v2将旧前缀助手整体改写为新版默认指令含工具调用分析流程、{{current_date}}占位符并补齐默认采样参数v3仅当助手指令与 v2 写入的默认文本逐字完全一致时剥离身份前缀段落恢复为纯默认指令用户自定义提示词不受影响迁移逻辑刻意保守每一档都先做字符串精确匹配再改写且失败时只logger.error而不抛出确保单个助手损坏不会阻塞整个扩展启动。这种“版本号文件 条件式补跑 精确匹配保护用户数据”的模式可直接移植到任何需要持久化状态演进的 Jan 扩展中。八、测试实践用内存文件系统验证扩展逻辑模板自带 src/index.test.ts展示了官方推荐的扩展测试方式用 vitest 对janhq/core的fs进行 mock以两个内存容器模拟虚拟文件系统——let files: Mapstring, string // 路径 - 文件内容 let dirs: Setstring // 已存在的目录existsSync / mkdir / writeFileSync / readFileSync / rm / readdirSync全部落到Map/Set上L12-L45使得测试完全不依赖真实磁盘。测试覆盖的断言点恰好对应第六、七节的所有行为getAssistants目录不存在、目录为空时均回退默认助手id: jan能并行读取多个助手跳过无assistant.json的孤儿目录与 JSON 损坏的条目createAssistant自动建目录并写入格式化 JSON断言输出含\n缩进目录已存在时不再调用mkdirdeleteAssistant存在时删除文件不存在时fs.rm根本不被调用onLoad自动创建file://assistants目录、写入迁移版本3、种子助手携带正确的默认参数、且不覆盖已持久化的自定义助手迁移v1 精确替换前缀且保留尾部自定义文本You are a helpful AI assistant. Be concise.→You are Jan, a helpful AI assistant. Be concise.v2 写入参数、v3 剥掉身份前缀已经是版本 3 时不重复执行迁移版本文件内容为garbage时按 0 处理并补跑。运行方式即npm test对应test: vitest run测试环境由 vitest.config.ts 与 src/test/setup.ts 配置。九、动手清单从模板到自定义扩展综合 README 与源码把模板改造为自己的扩展可以按以下清单执行改名与元数据更新 package.json 的name、productName、description、author并提升version替换源码src/是扩展的心脏README 明确允许整体替换。自定义扩展类应继承 core 中与你目标能力对应的抽象基类如AssistantExtension实现其全部抽象方法并在onLoad()中完成初始化、onUnload()中做清理遵循异步约定所有扩展函数按异步风格编写返回Promise需要响应消息等应用事件时使用janhq/core的events.on(...)订阅持久化走 file:// 协议使用 core 导出的fs与joinPath管理数据目录避免直接操作宿主磁盘构建与验证执行npm install后运行build即 README 所述的打包步骤确认dist/index.js生成用npm pack或仓库内的build:publish生成.tgz产物回归测试参照 src/index.test.ts 的内存 fs mock 手法为你的存储与迁移逻辑补测试再执行npm test。以上流程均以当前仓库的实际文件为准模板文档见 extensions/assistant-extension/README.md构建与类型配置见 rolldown.config.mjs、tsconfig.json扩展契约见 core/src/browser/extension.ts 与 core/src/browser/extensions/assistant.ts助手类型契约见 core/src/types/assistant/assistantEntity.ts。掌握这套“元数据 打包 生命周期 虚拟文件系统持久化 版本化迁移”的组合模式即可在 Jan 生态中开发并分发自己的功能扩展。【免费下载链接】janJan is an open source alternative to ChatGPT that runs 100% offline on your computer.项目地址: https://gitcode.com/GitHub_Trending/ja/jan创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表