Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"license": " AGPL-3.0",
"dependencies": {
"@dagrejs/dagre": "^1.1.4",
"@huggingface/transformers": "^3.5.2",
"@vicons/fluent": "^0.13.0",
"@vue-flow/core": "^1.42.5",
"better-sqlite3": "^11.10.0",
Expand Down
3 changes: 3 additions & 0 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import fs from "fs";
import {spawn} from "child_process";
import './hnswlib-service';
import './database'
import {runEmbedding} from "./model";

// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (started) {
Expand Down Expand Up @@ -293,3 +294,5 @@ ipcMain.handle('dialog:select-folder', async () => {
return result.filePaths[0]; // 返回选中的文件夹路径
}
});

ipcMain.handle('transformers:runEmbedding', runEmbedding)
46 changes: 46 additions & 0 deletions src/main/model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {env, FeatureExtractionPipeline, pipeline} from '@huggingface/transformers';
import {Tensor} from "@huggingface/transformers/types/utils/tensor";
import IpcMainInvokeEvent = Electron.IpcMainInvokeEvent;

const EMBEDDING_TASK = "feature-extraction";

class EmbeddingsPipeline {
static readonly model: string = 'Qwen3-Embedding-0.6B-ONNX';
private static instance: FeatureExtractionPipeline | null = null;

static async getInstance(): Promise<FeatureExtractionPipeline> {
if (this.instance === null) {
// Dynamically import the Transformers.js library
// Error: Could not dynamically require "../bin/napi-v3/darwin/arm64/onnxruntime_binding.node"
// let { pipeline, env } = await import('@huggingface/transformers');

// NOTE: Uncomment this to change the cache directory
// env.cacheDir = './.cache';

env.allowLocalModels = true; // 允许使用本地模型
env.allowRemoteModels = false; // 禁用远程模型下载
env.localModelPath = '/Users/johnzhang/git-repo/'; // 设置本地模型路径

this.instance = await pipeline<typeof EMBEDDING_TASK>(EMBEDDING_TASK, this.model, {
// quantized: true, // 明确使用量化模型: model_quantized.onnx
// model_file_name: "model",
dtype: 'q8',
// device: 'gpu' //如果支持
});
}
return this.instance;
}
}

async function runEmbedding(event: IpcMainInvokeEvent, text: string | string[]): Promise<any> {
const extractor = await EmbeddingsPipeline.getInstance();
const result: Tensor = await extractor(text, {pooling: 'mean', normalize: true});
console.log(result);
return {
data: result.data,
size: result.size,
location: result.location,
};
}

export { runEmbedding };
3 changes: 3 additions & 0 deletions src/main/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,8 @@ contextBridge.exposeInMainWorld('electronAPI', {
runQuery: (sql:string, params: any[]) => ipcRenderer.invoke('db:runQuery', sql, params),
fetchAll: (sql:string, params: any[]) => ipcRenderer.invoke('db:fetchAll', sql, params),
fetchOne: (sql:string, params: any[]) => ipcRenderer.invoke('db:fetchOne', sql, params),

// Embedding
runEmbedding: (text: string | string[]) => ipcRenderer.invoke('transformers:runEmbedding', text)
});

34 changes: 34 additions & 0 deletions src/renderer/pages/EmbeddingExample.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<template>
<n-card>
<n-space vertical>
<n-input v-model:value="content" type="text" placeholder="基本的 Input" />
<n-input
v-model:value="embeddingValue"
type="textarea"
placeholder="Output"
rows="20"
/>
<n-button @click="testEmbedding">转换</n-button>
</n-space>
</n-card>
</template>
<script setup lang="ts">
import {ref} from "vue";

const content = ref<string>('你好World');
const embeddingValue = ref('')

async function testEmbedding() {
if (!content.value) {
return
}
embeddingValue.value = '';
const result = await window.electronAPI.runEmbedding(content.value);
console.log(result)
embeddingValue.value = JSON.stringify(result, null, 2);
}
</script>

<style scoped>

</style>
7 changes: 6 additions & 1 deletion src/renderer/pages/home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<n-button @click="goPlay">play</n-button>
<n-button @click="goPlay1">search test</n-button>
<n-button @click="goPlay3">scan</n-button>
<n-button @click="tryEmbedding">Embedding Example</n-button>
<n-button @click="codeDiff">diff</n-button>

<!-- 创建项目对话框 -->
Expand Down Expand Up @@ -102,6 +103,10 @@ const goPlay3= () => {
router.push(`/play3`)
};

const tryEmbedding= () => {
router.push(`/embedding-example`)
};

const handleEditProject= (project) => {
// 打开编辑对话框,并预填项目信息
// ...
Expand Down Expand Up @@ -155,4 +160,4 @@ onMounted(async () => {
.body {
padding: 20px;
}
</style>
</style>
5 changes: 5 additions & 0 deletions src/renderer/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ const router = createRouter({
name: 'play3',
component: () => import('../pages/task-run-test.vue'),
},
{
path: '/embedding-example',
name: 'embedding-example',
component: () => import('../pages/EmbeddingExample.vue'),
},
{
path: '/diff',
name: 'diff',
Expand Down
4 changes: 3 additions & 1 deletion src/renderer/types/electron-api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ interface ElectronAPI {
runQuery: (sql: string, params?: any[] | Record<string, any>) => Promise<any>;
fetchAll: (sql: string, params?: any[] | Record<string, any>) => Promise<any[]>;
fetchOne: (sql: string, params?: any[] | Record<string, any>) => Promise<any>;

runEmbedding: (text: string | string[]) => Promise<any>;
}

declare interface Window {
electronAPI: ElectronAPI;
}
}
4 changes: 2 additions & 2 deletions vite.main.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { defineConfig } from 'vite';
export default defineConfig({
build: {
rollupOptions: {
external: ['hnswlib-node','better-sqlite3'], // 👈 关键配置:排除原生模块
external: ['hnswlib-node','better-sqlite3', "@huggingface/transformers"], // 👈 关键配置:排除原生模块
},
},
});
});