展開發(fā)指南:用 TypeScript 構(gòu)建、打包并測(cè)試你自己的 Jan 擴(kuò)展)
Jan Assistant 擴(kuò)展開發(fā)指南用 TypeScript 構(gòu)建、打包并測(cè)試你自己的 Jan 擴(kuò)展【免費(fèi)下載鏈接】janJan is an open source alternative to ChatGPT that runs 100% offline on your computer.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/ja/jan本文以 Jan 倉(cāng)庫(kù)中的 assistant-extension 模板 為核心結(jié)合 core 包中的擴(kuò)展基類、擴(kuò)展源碼 與 單元測(cè)試完整講解如何用 TypeScript 創(chuàng)建、打包、安裝和演進(jìn)一個(gè) Jan 擴(kuò)展從package.json元數(shù)據(jù)定義、rolldown 構(gòu)建產(chǎn)物到file://虛擬文件系統(tǒng)中的助手?jǐn)?shù)據(jù)遷移機(jī)制讀者可據(jù)此獨(dú)立開發(fā)自己的 Jan 擴(kuò)展。一、模板定位assistant-extension 在 Jan 倉(cāng)庫(kù)中的角色Jan 是運(yùn)行在本地的開源 AI 聊天應(yīng)用其功能通過“擴(kuò)展Extension”機(jī)制進(jìn)行模塊化拆分助手管理、對(duì)話編排、推理后端、模型下載、RAG、向量庫(kù)等能力都實(shí)現(xiàn)為獨(dú)立擴(kuò)展由janhq/core包提供統(tǒng)一的事件、文件系統(tǒng)和類型系統(tǒng)。extensions/assistant-extension 目錄既是 Jan 內(nèi)置的默認(rèn) AI 助手實(shí)現(xiàn)也被官方 README 明確定位為一個(gè)可直接 fork 使用的擴(kuò)展腳手架模板。README 開篇即說明Use this template to bootstrap the creation of a TypeScript Jan extension.因此圍繞該目錄學(xué)習(xí)既能掌握“如何寫一個(gè)新擴(kuò)展”又能順帶讀懂 Jan 默認(rèn)助手默認(rèn)系統(tǒng)提示詞、默認(rèn)采樣參數(shù)、助手持久化與數(shù)據(jù)遷移的完整實(shí)現(xiàn)。二、創(chuàng)建你自己的擴(kuò)展模板使用與初始環(huán)境搭建README 給出了標(biāo)準(zhǔn)的模板使用流程點(diǎn)擊倉(cāng)庫(kù)頂部的 Use this template 按鈕選擇 Create a new repository為新的倉(cāng)庫(kù)選擇 owner 與名稱點(diǎn)擊 Create repository克隆你的新倉(cāng)庫(kù)到本地。環(huán)境要求模板 README 明確要求一個(gè)較新的 Node.js 環(huán)境20.x 或更高版本如果在使用nodenv/nvm這類版本管理器可以在倉(cāng)庫(kù)根目錄按package.json中指定的版本安裝對(duì)應(yīng) Node。需要說明的一個(gè)倉(cāng)庫(kù)事實(shí)是當(dāng)前 package.json 中聲明了packageManager: yarn4.5.3且依賴使用了workspace:*協(xié)議janhq/core: workspace:*這意味著在 Jan monorepo 內(nèi)部開發(fā)時(shí)應(yīng)使用 Yarn 4 工作區(qū)而在 fork 出的獨(dú)立模板倉(cāng)庫(kù)中按 README 使用npm install同樣可行獨(dú)立倉(cāng)庫(kù)中janhq/core會(huì)解析為 npm 發(fā)布版本。依賴安裝、打包與產(chǎn)物檢查README 描述的三步工作流以及當(dāng)前倉(cāng)庫(kù)中對(duì)應(yīng)的真實(shí)腳本# 1. 安裝依賴 npm install # 2. 打包 TypeScriptREADME 中的命令當(dāng)前倉(cāng)庫(kù)腳本名為 build npm run bundle # 對(duì)應(yīng)當(dāng)前 package.json 中的 build: rolldown -c rolldown.config.mjs # 3. 檢查產(chǎn)物擴(kuò)展目錄中會(huì)出現(xiàn) .tgz 文件對(duì)照當(dāng)前 package.json 的scripts可確認(rèn)模板命令與倉(cāng)庫(kù)實(shí)際腳本的對(duì)應(yīng)關(guān)系{ 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在清理舊產(chǎn)物后執(zhí)行build再用npm pack生成.tgz安裝包并復(fù)制到倉(cāng)庫(kù)的pre-install目錄隨 Jan 應(yīng)用預(yù)裝test運(yùn)行 vitest 測(cè)試。README 中提到的 “tgz 產(chǎn)物”即由npm pack產(chǎn)生。三、擴(kuò)展元數(shù)據(jù)package.json 字段逐項(xiàng)解讀README 指出package.json定義了擴(kuò)展的名稱、主入口、描述和版本等元數(shù)據(jù)fork 模板后必須更新其中的name與description。以當(dāng)前模板文件為參照各關(guān)鍵字段含義如下字段當(dāng)前值作用namejanhq/assistant-extension擴(kuò)展包名是 Jan 識(shí)別擴(kuò)展的唯一標(biāo)識(shí)productNameJan Assistant展示在產(chǎn)品界面中的名稱version1.0.2擴(kuò)展版本號(hào)maindist/index.js擴(kuò)展主入口打包產(chǎn)物路徑nodedist/node/index.js節(jié)點(diǎn)側(cè)入口如存在獨(dú)立后端邏輯author/licenseJan servicejan.ai/AGPL-3.0作者與協(xié)議信息dependenciesjanhq/core唯一運(yùn)行時(shí)依賴Jan 擴(kuò)展核心包filesdist/*,package.json,README.md發(fā)布進(jìn).tgz包的文件白名單installConfig.hoistingLimitsworkspaces在 monorepo 中避免依賴被提升hoisting到工作區(qū)外這些字段并非擺設(shè)rolldown 構(gòu)建配置會(huì)直接讀取它們見下一節(jié)core 包的BaseExtension構(gòu)造參數(shù)name / productName / url / active / description / version見 extension.ts也與元數(shù)據(jù)一一對(duì)應(yīng)。四、構(gòu)建管線從 src/index.ts 到 dist/index.jsrolldown.config.mjs 完整展示了擴(kuò)展的打包邏輯值得逐行理解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), }, } ])要點(diǎn)入口是src/index.ts輸出為ESM 格式單文件dist/index.js正好落在package.json的main聲明位置platform: browser表明擴(kuò)展代碼運(yùn)行在瀏覽器/前端運(yùn)行時(shí)環(huán)境中define在編譯期注入了兩個(gè)全局常量NODE與VERSION其值來自package.json的name/node/version字段。這與 src/types/global.d.ts 中的聲明相呼應(yīng)declare const NODE: string declare const VERSION: string也就是說擴(kuò)展代碼在運(yùn)行時(shí)可以直接讀取自身的包名與版本號(hào)無需額外配置。tsconfig.json 則規(guī)定了編譯口徑target: es2016、module: ES6、declaration: true聲明文件輸出到dist/types、sourceMap: true與 ESM 打包目標(biāo)保持一致。五、擴(kuò)展代碼骨架繼承 AssistantExtension 與生命周期模板 README 對(duì)擴(kuò)展代碼有兩點(diǎn)核心提示大部分 Jan 擴(kuò)展函數(shù)都是異步處理的擴(kuò)展函數(shù)會(huì)返回Promiseany事件訂閱的典型寫法如下摘自 READMEimport { events, MessageEvent, MessageRequest } from janhq/core function onStart(): Promiseany { return events.on(MessageEvent.OnMessageSent, (data: MessageRequest) this.inference(data) ) }在 core 包中擴(kuò)展體系以抽象類層次組織BaseExtension所有擴(kuò)展的基類定義了name、url、active、description、version等屬性以及兩個(gè)必須實(shí)現(xiàn)的生命周期鉤子onLoad()/onUnload()還提供了registerModels、registerSettings等通用能力AssistantExtension助手類型擴(kuò)展的抽象中間層聲明type()返回ExtensionTypeEnum.Assistant并要求實(shí)現(xiàn)三個(gè)抽象方法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的數(shù)據(jù)形狀定義在 core/src/types/assistant/assistantEntity.ts包含avatar、id、object、created_at、name、description、model、instructions、tools、file_ids、metadata等字段并配有逐字段注釋是編寫助手相關(guān)擴(kuò)展時(shí)最核心的類型契約。模板 README 指向的 Jan Extension Core 模塊文檔在本倉(cāng)庫(kù)中即 core/README.md。六、默認(rèn)助手實(shí)現(xiàn)解析onLoad、持久化與種子數(shù)據(jù)extensions/assistant-extension/src/index.ts 中的JanAssistantExtension是模板的參考實(shí)現(xiàn)onLoad()L17-L39展示了擴(kuò)展加載時(shí)應(yīng)當(dāng)完成的標(biāo)準(zhǔn)初始化序列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) } }這里體現(xiàn)了 Jan 擴(kuò)展編程模型的三個(gè)關(guān)鍵特征虛擬文件系統(tǒng)一切持久化都通過file://前綴路徑進(jìn)行如file://assistants、file://assistants/id/assistant.json由janhq/core導(dǎo)出的fs模塊統(tǒng)一抽象屏蔽了不同平臺(tái)的真實(shí)磁盤差異。這是一個(gè)寫自定義擴(kuò)展時(shí)必須記住的約定——不要直接使用 Node 的fs模塊。冪等初始化先確保目錄存在再執(zhí)行遷移最后僅在磁盤為空時(shí)寫入種子數(shù)據(jù)避免覆蓋用戶已自定義的助手對(duì)應(yīng)測(cè)試用例 “does not overwrite an existing persisted assistant on load”。種子參數(shù)默認(rèn)助手Janid: jan、avatar: 、model: *表示適配所有已安裝模型附帶默認(rèn)采樣參數(shù)temperature: 0.7 / top_k: 20 / top_p: 0.8 / repeat_penalty: 1.12其instructions是一段要求“按用戶語(yǔ)言回復(fù)、逐步推理、作為專業(yè)工具調(diào)用者分析信息缺口”的系統(tǒng)提示詞并帶有{{current_date}}日期占位符tools中默認(rèn)掛了一個(gè)禁用狀態(tài)的retrieval工具附帶top_k: 2、chunk_size: 1024、chunk_overlap: 64的 RAG 檢索配置與檢索提示詞模板L333-L351。CRUD 方法本身也非常短小是“最小可運(yùn)行擴(kuò)展”的范例createAssistantL281-L292確保file://assistants/id/目錄存在后把助手序列化為縮進(jìn) JSON 寫入assistant.jsondeleteAssistantL294-L303存在即刪除assistant.json不存在則為空操作no-opgetAssistantsL275-L279優(yōu)先讀取磁盤數(shù)據(jù)磁盤為空時(shí)回退到內(nèi)置的defaultAssistant保證上層調(diào)用總能拿到至少一個(gè)可用助手。私有方法readAssistantsFromDisk還會(huì)跳過缺少assistant.json的目錄以及 JSON 解析失敗的損壞文件只記錄錯(cuò)誤而不中斷整體加載。七、數(shù)據(jù)遷移機(jī)制版本化 .migration_version 與三級(jí)遷移JanAssistantExtension內(nèi)置了一套值得借鑒的輕量數(shù)據(jù)遷移方案L41-L95遷移版本記錄在file://assistants/.migration_version文件中當(dāng)前版本常量CURRENT_MIGRATION_VERSION 3getCurrentMigrationVersion()讀取該文件文件缺失或內(nèi)容無法解析parseInt得到NaN時(shí)一律按版本 0處理從而保證遷移一定會(huì)補(bǔ)跑runMigrations()按currentVersion N的條件逐檔執(zhí)行遷移每完成一檔立即寫回版本號(hào)版本遷移內(nèi)容v1將舊版指令前綴You are a helpful AI assistant.改寫為You are Jan, a helpful AI assistant.并保留后續(xù)自定義內(nèi)容用startsWith 字符串截取實(shí)現(xiàn)v2將舊前綴助手整體改寫為新版默認(rèn)指令含工具調(diào)用分析流程、{{current_date}}占位符并補(bǔ)齊默認(rèn)采樣參數(shù)v3僅當(dāng)助手指令與 v2 寫入的默認(rèn)文本逐字完全一致時(shí)剝離身份前綴段落恢復(fù)為純默認(rèn)指令用戶自定義提示詞不受影響遷移邏輯刻意保守每一檔都先做字符串精確匹配再改寫且失敗時(shí)只logger.error而不拋出確保單個(gè)助手損壞不會(huì)阻塞整個(gè)擴(kuò)展啟動(dòng)。這種“版本號(hào)文件 條件式補(bǔ)跑 精確匹配保護(hù)用戶數(shù)據(jù)”的模式可直接移植到任何需要持久化狀態(tài)演進(jìn)的 Jan 擴(kuò)展中。八、測(cè)試實(shí)踐用內(nèi)存文件系統(tǒng)驗(yàn)證擴(kuò)展邏輯模板自帶 src/index.test.ts展示了官方推薦的擴(kuò)展測(cè)試方式用 vitest 對(duì)janhq/core的fs進(jìn)行 mock以兩個(gè)內(nèi)存容器模擬虛擬文件系統(tǒng)——let files: Mapstring, string // 路徑 - 文件內(nèi)容 let dirs: Setstring // 已存在的目錄existsSync / mkdir / writeFileSync / readFileSync / rm / readdirSync全部落到Map/Set上L12-L45使得測(cè)試完全不依賴真實(shí)磁盤。測(cè)試覆蓋的斷言點(diǎn)恰好對(duì)應(yīng)第六、七節(jié)的所有行為getAssistants目錄不存在、目錄為空時(shí)均回退默認(rèn)助手id: jan能并行讀取多個(gè)助手跳過無assistant.json的孤兒目錄與 JSON 損壞的條目createAssistant自動(dòng)建目錄并寫入格式化 JSON斷言輸出含\n縮進(jìn)目錄已存在時(shí)不再調(diào)用mkdirdeleteAssistant存在時(shí)刪除文件不存在時(shí)fs.rm根本不被調(diào)用onLoad自動(dòng)創(chuàng)建file://assistants目錄、寫入遷移版本3、種子助手?jǐn)y帶正確的默認(rèn)參數(shù)、且不覆蓋已持久化的自定義助手遷移v1 精確替換前綴且保留尾部自定義文本You are a helpful AI assistant. Be concise.→You are Jan, a helpful AI assistant. Be concise.v2 寫入?yún)?shù)、v3 剝掉身份前綴已經(jīng)是版本 3 時(shí)不重復(fù)執(zhí)行遷移版本文件內(nèi)容為garbage時(shí)按 0 處理并補(bǔ)跑。運(yùn)行方式即npm test對(duì)應(yīng)test: vitest run測(cè)試環(huán)境由 vitest.config.ts 與 src/test/setup.ts 配置。九、動(dòng)手清單從模板到自定義擴(kuò)展綜合 README 與源碼把模板改造為自己的擴(kuò)展可以按以下清單執(zhí)行改名與元數(shù)據(jù)更新 package.json 的name、productName、description、author并提升version替換源碼src/是擴(kuò)展的心臟README 明確允許整體替換。自定義擴(kuò)展類應(yīng)繼承 core 中與你目標(biāo)能力對(duì)應(yīng)的抽象基類如AssistantExtension實(shí)現(xiàn)其全部抽象方法并在onLoad()中完成初始化、onUnload()中做清理遵循異步約定所有擴(kuò)展函數(shù)按異步風(fēng)格編寫返回Promise需要響應(yīng)消息等應(yīng)用事件時(shí)使用janhq/core的events.on(...)訂閱持久化走 file:// 協(xié)議使用 core 導(dǎo)出的fs與joinPath管理數(shù)據(jù)目錄避免直接操作宿主磁盤構(gòu)建與驗(yàn)證執(zhí)行npm install后運(yùn)行build即 README 所述的打包步驟確認(rèn)dist/index.js生成用npm pack或倉(cāng)庫(kù)內(nèi)的build:publish生成.tgz產(chǎn)物回歸測(cè)試參照 src/index.test.ts 的內(nèi)存 fs mock 手法為你的存儲(chǔ)與遷移邏輯補(bǔ)測(cè)試再執(zhí)行npm test。以上流程均以當(dāng)前倉(cāng)庫(kù)的實(shí)際文件為準(zhǔn)模板文檔見 extensions/assistant-extension/README.md構(gòu)建與類型配置見 rolldown.config.mjs、tsconfig.json擴(kuò)展契約見 core/src/browser/extension.ts 與 core/src/browser/extensions/assistant.ts助手類型契約見 core/src/types/assistant/assistantEntity.ts。掌握這套“元數(shù)據(jù) 打包 生命周期 虛擬文件系統(tǒng)持久化 版本化遷移”的組合模式即可在 Jan 生態(tài)中開發(fā)并分發(fā)自己的功能擴(kuò)展?!久赓M(fèi)下載鏈接】janJan is an open source alternative to ChatGPT that runs 100% offline on your computer.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/ja/jan創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考