feat(provider-transformers): added package to integrate transformers.js with xsai (#36)
* feat(provider-transformers): added package to integrate transformers.js with xsai * fix: build * chore: use updated xsai --------- Signed-off-by: Neko Ayaka <neko@ayaka.moe>
This commit is contained in:
@@ -18,7 +18,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@tresjs/core": "^4.3.3",
|
||||
"@unocss/reset": "^66.1.0-beta.1",
|
||||
"@unocss/reset": "^66.1.0-beta.3",
|
||||
"@vueuse/core": "^12.7.0",
|
||||
"@vueuse/motion": "^2.2.6",
|
||||
"ofetch": "^1.4.1",
|
||||
|
||||
@@ -48,8 +48,8 @@
|
||||
"@tresjs/core": "^4.3.3",
|
||||
"@types/yauzl": "^2.10.3",
|
||||
"@typeschema/valibot": "^0.14.0",
|
||||
"@unhead/vue": "^2.0.0-alpha.23",
|
||||
"@unocss/reset": "^66.1.0-beta.1",
|
||||
"@unhead/vue": "^2.0.0-alpha.27",
|
||||
"@unocss/reset": "^66.1.0-beta.3",
|
||||
"@vueuse/core": "^12.7.0",
|
||||
"@vueuse/head": "^2.0.0",
|
||||
"@vueuse/shared": "^12.7.0",
|
||||
@@ -59,7 +59,7 @@
|
||||
"@xsai/providers": "catalog:",
|
||||
"@xsai/shared-chat": "catalog:",
|
||||
"@xsai/stream-text": "catalog:",
|
||||
"@xsai/utils-chat": "^0.1.0-beta.7",
|
||||
"@xsai/utils-chat": "^0.1.0-beta.9",
|
||||
"defu": "^6.1.4",
|
||||
"nprogress": "^0.2.0",
|
||||
"ofetch": "^1.4.1",
|
||||
@@ -85,9 +85,9 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-toolkit/tsconfig": "^1.0.1",
|
||||
"@iconify-json/carbon": "^1.2.7",
|
||||
"@iconify-json/carbon": "^1.2.8",
|
||||
"@iconify-json/eos-icons": "^1.2.2",
|
||||
"@iconify-json/lucide": "^1.2.26",
|
||||
"@iconify-json/lucide": "^1.2.27",
|
||||
"@iconify-json/mingcute": "^1.2.3",
|
||||
"@iconify-json/solar": "^1.2.2",
|
||||
"@iconify-json/svg-spinners": "^1.2.2",
|
||||
@@ -103,14 +103,14 @@
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"@vue-macros/volar": "^0.30.14",
|
||||
"@vueuse/motion": "^2.2.6",
|
||||
"electron": "^34.2.0",
|
||||
"electron": "^34.3.0",
|
||||
"electron-builder": "24.13.3",
|
||||
"electron-vite": "^2.3.0",
|
||||
"markdown-it-link-attributes": "^4.0.1",
|
||||
"unocss-preset-scrollbar": "^3.2.0",
|
||||
"unplugin-auto-import": "^19.1.0",
|
||||
"unplugin-vue-components": "^28.4.0",
|
||||
"unplugin-vue-macros": "^2.14.3",
|
||||
"unplugin-auto-import": "^19.1.1",
|
||||
"unplugin-vue-components": "^28.4.1",
|
||||
"unplugin-vue-macros": "^2.14.4",
|
||||
"unplugin-vue-markdown": "^28.3.1",
|
||||
"unplugin-vue-router": "^0.11.2",
|
||||
"vite-bundle-visualizer": "^1.2.1",
|
||||
|
||||
@@ -44,8 +44,8 @@
|
||||
"@tresjs/core": "^4.3.3",
|
||||
"@types/yauzl": "^2.10.3",
|
||||
"@typeschema/valibot": "^0.14.0",
|
||||
"@unhead/vue": "^2.0.0-alpha.23",
|
||||
"@unocss/reset": "^66.1.0-beta.1",
|
||||
"@unhead/vue": "^2.0.0-alpha.27",
|
||||
"@unocss/reset": "^66.1.0-beta.3",
|
||||
"@valibot/to-json-schema": "1.0.0-rc.0",
|
||||
"@vueuse/core": "^12.7.0",
|
||||
"@vueuse/head": "^2.0.0",
|
||||
@@ -56,7 +56,7 @@
|
||||
"@xsai/providers": "catalog:",
|
||||
"@xsai/shared-chat": "catalog:",
|
||||
"@xsai/stream-text": "catalog:",
|
||||
"@xsai/utils-chat": "^0.1.0-beta.7",
|
||||
"@xsai/utils-chat": "catalog:",
|
||||
"defu": "^6.1.4",
|
||||
"jszip": "^3.10.1",
|
||||
"nprogress": "^0.2.0",
|
||||
@@ -83,9 +83,9 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-toolkit/preload": "^3.0.1",
|
||||
"@iconify-json/carbon": "^1.2.7",
|
||||
"@iconify-json/carbon": "^1.2.8",
|
||||
"@iconify-json/eos-icons": "^1.2.2",
|
||||
"@iconify-json/lucide": "^1.2.26",
|
||||
"@iconify-json/lucide": "^1.2.27",
|
||||
"@iconify-json/mingcute": "^1.2.3",
|
||||
"@iconify-json/simple-icons": "^1.2.26",
|
||||
"@iconify-json/solar": "^1.2.2",
|
||||
@@ -105,9 +105,9 @@
|
||||
"@vueuse/motion": "^2.2.6",
|
||||
"hfup": "workspace:^",
|
||||
"markdown-it-link-attributes": "^4.0.1",
|
||||
"unplugin-auto-import": "^19.1.0",
|
||||
"unplugin-vue-components": "^28.4.0",
|
||||
"unplugin-vue-macros": "^2.14.3",
|
||||
"unplugin-auto-import": "^19.1.1",
|
||||
"unplugin-vue-components": "^28.4.1",
|
||||
"unplugin-vue-macros": "^2.14.4",
|
||||
"unplugin-vue-markdown": "^28.3.1",
|
||||
"unplugin-vue-router": "^0.11.2",
|
||||
"vite-bundle-visualizer": "^1.2.1",
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"typecheck": "vue-tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@unocss/reset": "^66.1.0-beta.1",
|
||||
"@unocss/reset": "^66.1.0-beta.3",
|
||||
"@vueuse/core": "^12.7.0",
|
||||
"ofetch": "^1.4.1",
|
||||
"vue": "^3.5.13"
|
||||
|
||||
+5
-5
@@ -2,7 +2,7 @@
|
||||
"name": "airi-vtuber",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@10.5.0",
|
||||
"packageManager": "pnpm@10.5.2",
|
||||
"description": "LLM powered virtual character",
|
||||
"author": {
|
||||
"name": "Neko Ayaka",
|
||||
@@ -35,8 +35,8 @@
|
||||
"@antfu/ni": "^23.3.1",
|
||||
"@cspell/dict-ru_ru": "^2.2.4",
|
||||
"@types/node": "^22.13.5",
|
||||
"@unocss/eslint-config": "^66.1.0-beta.1",
|
||||
"@unocss/eslint-plugin": "^66.1.0-beta.1",
|
||||
"@unocss/eslint-config": "^66.1.0-beta.3",
|
||||
"@unocss/eslint-plugin": "^66.1.0-beta.3",
|
||||
"@vitest/coverage-v8": "3.0.5",
|
||||
"bumpp": "^10.0.3",
|
||||
"changelogithub": "^13.12.1",
|
||||
@@ -50,9 +50,9 @@
|
||||
"taze": "^18.6.0",
|
||||
"typescript": "~5.7.3",
|
||||
"unbuild": "3.0.0-rc.11",
|
||||
"unocss": "^66.1.0-beta.1",
|
||||
"unocss": "^66.1.0-beta.3",
|
||||
"vite": "^6.2.0",
|
||||
"vite-plugin-inspect": "^10.3.0",
|
||||
"vite-plugin-inspect": "^11.0.0",
|
||||
"vitest": "^3.0.7"
|
||||
},
|
||||
"workspaces": [
|
||||
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
[build]
|
||||
base = "/"
|
||||
command = "pnpm -F @proj-airi/provider-transformers... run build"
|
||||
publish = "/packages/provider-transformers/playground/dist"
|
||||
|
||||
[build.environment]
|
||||
NODE_VERSION = "23"
|
||||
|
||||
[[redirects]]
|
||||
from = "/*"
|
||||
to = "/index.html"
|
||||
status = 200
|
||||
force = false
|
||||
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"name": "@proj-airi/provider-transformers",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"description": "Transformers.js wrapper makes it easy to use with xsai",
|
||||
"author": {
|
||||
"name": "Neko Ayaka",
|
||||
"email": "neko@ayaka.moe",
|
||||
"url": "https://github.com/nekomeowww"
|
||||
},
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/moeru-ai/airi.git",
|
||||
"directory": "packages/provider-transformers"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.mjs"
|
||||
},
|
||||
"./worker": {
|
||||
"types": "./dist/worker/index.d.ts",
|
||||
"import": "./dist/worker/index.mjs"
|
||||
},
|
||||
"./types": {
|
||||
"types": "./dist/types/index.d.ts",
|
||||
"import": "./dist/types/index.mjs"
|
||||
}
|
||||
},
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"README.md",
|
||||
"dist",
|
||||
"package.json"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "pnpm run stub && pnpm run play:dev",
|
||||
"stub": "unbuild --stub",
|
||||
"build": "unbuild && pnpm run play:build",
|
||||
"play:dev": "vite",
|
||||
"play:build": "vite build",
|
||||
"play:preview": "vite preview",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"web-worker": "^1.5.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"web-worker": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@huggingface/transformers": "^3.3.3",
|
||||
"@proj-airi/utils-transformers": "workspace:^",
|
||||
"@webgpu/types": "^0.1.54",
|
||||
"@xsai-ext/shared-providers": "catalog:",
|
||||
"@xsai/embed": "catalog:",
|
||||
"@xsai/shared": "catalog:",
|
||||
"defu": "^6.1.4",
|
||||
"es-toolkit": "^1.32.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify-json/solar": "^1.2.2",
|
||||
"@unocss/reset": "^66.1.0-beta.3",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"@vueuse/core": "^12.7.0",
|
||||
"vite": "^6.2.0",
|
||||
"vue": "^3.5.13",
|
||||
"vue-tsc": "^2.2.4"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Project AIRI 🤗 Transformers.js xsai provider Playground</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0" />
|
||||
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<script>
|
||||
;(function () {
|
||||
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
const setting = localStorage.getItem('vueuse-color-scheme') || 'auto'
|
||||
if (setting === 'dark' || (prefersDark && setting !== 'light'))
|
||||
document.documentElement.classList.toggle('dark', true)
|
||||
})()
|
||||
</script>
|
||||
</head>
|
||||
<body class="font-sans">
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
<noscript> This website requires JavaScript to function properly. Please enable JavaScript to continue. </noscript>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.dev/svgjs" width="24" height="24"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<g fill="none">
|
||||
<path d="m12.594 23.258l-.012.002l-.071.035l-.02.004l-.014-.004l-.071-.036q-.016-.004-.024.006l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.016-.018m.264-.113l-.014.002l-.184.093l-.01.01l-.003.011l.018.43l.005.012l.008.008l.201.092q.019.005.029-.008l.004-.014l-.034-.614q-.005-.019-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.003-.011l.018-.43l-.003-.012l-.01-.01z"></path>
|
||||
<path fill="#fd7f9c" d="M18.296 3.045a1 1 0 0 1 .657.652l.03.119l1.341 7.154q.154.826.148 1.63l-.012.4H21a1 1 0 0 1 .117 1.993L21 15h-.894q-.164.531-.392 1.033l-.16.33l1.07.856a1 1 0 0 1-1.146 1.634l-.103-.072l-.936-.749A8.43 8.43 0 0 1 12 21a8.42 8.42 0 0 1-6.25-2.755l-.19-.213l-.935.749a1 1 0 0 1-1.343-1.477l.093-.085l1.07-.856a9 9 0 0 1-.435-1.012L3.894 15H3a1 1 0 0 1-.117-1.993L3 13h.54a8.5 8.5 0 0 1 .069-1.619l.067-.411l1.341-7.154a1 1 0 0 1 1.598-.604l.092.08l2.414 2.415a1 1 0 0 0 .576.284L9.828 6h4.344a1 1 0 0 0 .608-.206l.099-.087l2.414-2.414a1 1 0 0 1 1.003-.248m-.93 3.003L16.293 7.12a3 3 0 0 1-2.121.88H9.828a3 3 0 0 1-2.12-.879L6.632 6.048l-.992 5.29A6.5 6.5 0 0 0 5.545 13H7a1 1 0 1 1 0 2h-.492a.998.998 0 0 1 .71 1.696l-.095.086A6.44 6.44 0 0 0 12 19a6.43 6.43 0 0 0 4.696-2.02l.18-.2a1 1 0 0 1 .616-1.78H17a1 1 0 1 1 0-2h1.455a6.5 6.5 0 0 0-.096-1.662zm-3.472 9.005a1 1 0 0 1-.447 1.342l-.553.276a2 2 0 0 1-1.788 0l-.553-.276a1 1 0 0 1 .894-1.79l.553.277l.553-.276a1 1 0 0 1 1.341.447M9.5 10a1.5 1.5 0 1 1 0 3a1.5 1.5 0 0 1 0-3m5 0a1.5 1.5 0 1 1 0 3a1.5 1.5 0 0 1 0-3"></path>
|
||||
</g>
|
||||
</svg><style>@media (prefers-color-scheme: light) { :root { filter: none; } }
|
||||
@media (prefers-color-scheme: dark) { :root { filter: none; } }
|
||||
</style></svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,105 @@
|
||||
<script setup lang="ts">
|
||||
import { useDark, useToggle } from '@vueuse/core'
|
||||
import { embed } from '@xsai/embed'
|
||||
import { serialize } from 'superjson'
|
||||
import { onMounted, ref } from 'vue'
|
||||
|
||||
import { createTransformers } from '../../src'
|
||||
import embedWorkerURL from '../../src/worker?url'
|
||||
|
||||
const isDark = useDark()
|
||||
const toggleDark = useToggle(isDark)
|
||||
|
||||
const modelId = ref('Xenova/all-MiniLM-L6-v2')
|
||||
const input = ref('Hello, world!')
|
||||
const results = ref<any>()
|
||||
|
||||
const transformersProvider = createTransformers({ embedWorkerURL })
|
||||
|
||||
onMounted(async () => {
|
||||
await transformersProvider.loadEmbed(modelId.value)
|
||||
})
|
||||
|
||||
async function execute() {
|
||||
const result = await embed({
|
||||
...transformersProvider.embed(modelId.value),
|
||||
model: modelId.value,
|
||||
input: input.value,
|
||||
})
|
||||
|
||||
results.value = result
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div flex flex-col gap-2 p-4>
|
||||
<header flex flex-row items-center justify-between>
|
||||
<h1 text-2xl>
|
||||
<a href="https://huggingface.co/docs/transformers.js/index">🤗 Transformers.js</a> + <a
|
||||
href="https://github.com/moeru-ai/xsai"
|
||||
>xsai</a> Playground
|
||||
</h1>
|
||||
<button text-lg @click="() => toggleDark()">
|
||||
<div v-if="isDark" i-solar:moon-stars-bold-duotone />
|
||||
<div v-else i-solar:sun-bold />
|
||||
</button>
|
||||
</header>
|
||||
<div flex flex-col gap-2>
|
||||
<h2 text-xl>
|
||||
Options
|
||||
</h2>
|
||||
<div w-full flex flex-row gap-2>
|
||||
<div w-full flex flex-row gap-2>
|
||||
<label flex flex-row items-center gap-2>
|
||||
<div text-nowrap><span>Model ID</span></div>
|
||||
<input v-model="modelId" bg="neutral-100 dark:neutral-800" block min-w-full w-full rounded-lg p-2>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div grid grid-cols-2 gap-2>
|
||||
<div flex flex-col gap-2>
|
||||
<h2 text-xl>
|
||||
Inference
|
||||
</h2>
|
||||
<div>
|
||||
<textarea v-model="input" h-full w-full rounded-lg bg="neutral-100 dark:neutral-800" p-4 font-mono />
|
||||
</div>
|
||||
<div flex flex-row gap-2>
|
||||
<button rounded-lg bg="blue-100 dark:blue-900" px-4 py-2 @click="execute">
|
||||
Execute
|
||||
</button>
|
||||
</div>
|
||||
<div flex flex-col gap-2>
|
||||
<h2 text-xl>
|
||||
Results
|
||||
</h2>
|
||||
<div max-h-100 of-y-scroll whitespace-pre-wrap p-4 font-mono>
|
||||
{{ JSON.stringify(serialize(results).json, null, 2) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
|
||||
html {
|
||||
background: #fff;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
html.dark {
|
||||
background: #121212;
|
||||
color-scheme: dark;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createApp } from 'vue'
|
||||
|
||||
import App from './App.vue'
|
||||
import '@unocss/reset/tailwind.css'
|
||||
import 'uno.css'
|
||||
|
||||
createApp(App)
|
||||
.mount('#app')
|
||||
@@ -0,0 +1,107 @@
|
||||
import type { CreateProviderOptions, EmbedProviderWithExtraOptions } from '@xsai-ext/shared-providers'
|
||||
import type { EmbedResponse } from '@xsai/embed'
|
||||
import type { CommonRequestOptions } from '@xsai/shared'
|
||||
import type { LoadOptions, WorkerMessageEvent } from './types'
|
||||
|
||||
import { merge } from '@xsai-ext/shared-providers'
|
||||
|
||||
export type Loadable<P, T = string, T2 = undefined> = P & {
|
||||
loadEmbed: (model: (string & {}) | T, options?: T2) => Promise<void>
|
||||
}
|
||||
|
||||
export function createEmbedProvider<T extends string, T2 extends CommonRequestOptions & LoadOptions>(createOptions: CreateProviderOptions): Loadable<EmbedProviderWithExtraOptions<T, T2>, T, T2> {
|
||||
let worker: Worker
|
||||
let isReady = false
|
||||
|
||||
function loadModel(model: (string & {}) | T, options: T2) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
try {
|
||||
const workerURL = new URL(createOptions.baseURL)
|
||||
|
||||
if (!worker)
|
||||
worker = new Worker(workerURL.searchParams.get('worker-url')!, { type: 'module' })
|
||||
if (!worker)
|
||||
throw new Error('Worker not initialized')
|
||||
|
||||
worker.postMessage({ type: 'load', data: { modelId: model, task: 'feature-extraction', options } } satisfies WorkerMessageEvent)
|
||||
}
|
||||
catch (err) {
|
||||
reject(err)
|
||||
}
|
||||
|
||||
worker.onmessage = (event: MessageEvent<WorkerMessageEvent>) => {
|
||||
switch (event.data.type) {
|
||||
case 'error':
|
||||
reject(event.data.data.error)
|
||||
break
|
||||
case 'status':
|
||||
if (event.data.data.status === 'ready') {
|
||||
isReady = true
|
||||
resolve()
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
embed: (model, options) => Object.assign(createOptions, {
|
||||
fetch: (_, init: RequestInit) => {
|
||||
return new Promise<Response>((resolve, reject) => {
|
||||
loadModel(model, options).then(() => {
|
||||
if (!worker || !isReady) {
|
||||
reject(new Error('Model not loaded'))
|
||||
return
|
||||
}
|
||||
|
||||
let text: string = ''
|
||||
let body: any
|
||||
|
||||
try {
|
||||
body = JSON.parse(init.body.toString())
|
||||
text = body.input
|
||||
delete body.input
|
||||
}
|
||||
catch (err) {
|
||||
reject(err)
|
||||
return
|
||||
}
|
||||
|
||||
let errored = false
|
||||
let resultDone = false
|
||||
|
||||
worker.onmessage = (event: MessageEvent<WorkerMessageEvent>) => {
|
||||
switch (event.data.type) {
|
||||
case 'error':
|
||||
errored = true
|
||||
reject(event.data.data.error)
|
||||
break
|
||||
case 'extractResult':
|
||||
resultDone = true
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const result = { data: [{ embedding: event.data.data.output.data, index: 0, object: 'embedding' }], model, object: 'list', usage: { prompt_tokens: 0, total_tokens: 0 } } satisfies EmbedResponse
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const encoder = new TextEncoder()
|
||||
|
||||
resolve(new Response(encoder.encode(JSON.stringify(result))))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!errored && !resultDone)
|
||||
worker.postMessage({ type: 'extract', data: { text, options: body as any } } satisfies WorkerMessageEvent)
|
||||
})
|
||||
})
|
||||
},
|
||||
}) as unknown as T2,
|
||||
loadEmbed: loadModel,
|
||||
}
|
||||
}
|
||||
|
||||
export function createTransformers(options: { embedWorkerURL: string }) {
|
||||
return merge(
|
||||
createEmbedProvider<'Xenova/all-MiniLM-L6-v2', CreateProviderOptions & LoadOptions & { model: string }>({ baseURL: `xsai-provider-ext:///?worker-url=${options.embedWorkerURL}&other=` }),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import type { FeatureExtractionPipelineOptions, pipeline, ProgressInfo } from '@huggingface/transformers'
|
||||
import type { PipelineOptionsFrom } from '@proj-airi/utils-transformers/types'
|
||||
|
||||
export enum MessageStatus {
|
||||
Loading = 'loading',
|
||||
Ready = 'ready',
|
||||
}
|
||||
|
||||
export type LoadOptions = Omit<PipelineOptionsFrom<typeof pipeline<'feature-extraction'>>, 'progress_callback'>
|
||||
|
||||
export interface WorkerMessageBaseEvent<T, D> {
|
||||
type: T
|
||||
data: D
|
||||
}
|
||||
|
||||
export interface WorkerMessageEvents {
|
||||
load: {
|
||||
task: string
|
||||
modelId: string
|
||||
options?: LoadOptions
|
||||
}
|
||||
error: {
|
||||
error?: unknown
|
||||
message?: string
|
||||
}
|
||||
status: {
|
||||
status: MessageStatus
|
||||
message?: string
|
||||
}
|
||||
info: {
|
||||
message: string
|
||||
}
|
||||
progress: {
|
||||
progress: ProgressInfo
|
||||
}
|
||||
extract: {
|
||||
text: string | string[]
|
||||
options?: FeatureExtractionPipelineOptions
|
||||
}
|
||||
extractResult: {
|
||||
input: {
|
||||
text: string | string[]
|
||||
options?: FeatureExtractionPipelineOptions
|
||||
}
|
||||
output: {
|
||||
data: number[]
|
||||
dims: number[]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type WorkerMessageEvent = {
|
||||
[K in keyof WorkerMessageEvents]: WorkerMessageBaseEvent<K, WorkerMessageEvents[K]>;
|
||||
}[keyof WorkerMessageEvents]
|
||||
@@ -0,0 +1,13 @@
|
||||
export async function supportsWebGPU() {
|
||||
try {
|
||||
if (!('gpu' in navigator) || !navigator.gpu)
|
||||
return false
|
||||
|
||||
await navigator.gpu.requestAdapter()
|
||||
return true
|
||||
}
|
||||
catch (e) {
|
||||
console.error(e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/* eslint-disable no-restricted-globals */
|
||||
import type { FeatureExtractionPipeline, FeatureExtractionPipelineOptions } from '@huggingface/transformers'
|
||||
import type { PipelineOptionsFrom } from '@proj-airi/utils-transformers/types'
|
||||
import type { WorkerMessageEvent } from '../types'
|
||||
|
||||
import { pipeline } from '@huggingface/transformers'
|
||||
import { defu } from 'defu'
|
||||
|
||||
import { MessageStatus } from '../types'
|
||||
import { supportsWebGPU } from '../utils'
|
||||
|
||||
let embed: FeatureExtractionPipeline
|
||||
|
||||
async function load(modelId: string, options?: Omit<PipelineOptionsFrom<typeof pipeline<'feature-extraction'>>, 'progress_callback'>) {
|
||||
try {
|
||||
const device = (await supportsWebGPU()) ? 'webgpu' : 'wasm'
|
||||
|
||||
const opts = defu<PipelineOptionsFrom<typeof pipeline<'feature-extraction'>>, PipelineOptionsFrom<typeof pipeline<'feature-extraction'>>[]>(options, {
|
||||
device,
|
||||
progress_callback: (progress) => {
|
||||
self.postMessage({ type: 'progress', data: { progress } } satisfies WorkerMessageEvent)
|
||||
},
|
||||
})
|
||||
|
||||
self.postMessage({ type: 'info', data: { message: `Using device: "${device}"` } } satisfies WorkerMessageEvent)
|
||||
self.postMessage({ type: 'info', data: { message: 'Loading models...' } } satisfies WorkerMessageEvent)
|
||||
|
||||
embed = await pipeline('feature-extraction', modelId, opts)
|
||||
|
||||
self.postMessage({ type: 'status', data: { status: MessageStatus.Ready, message: 'Ready!' } } satisfies WorkerMessageEvent)
|
||||
}
|
||||
catch (err) {
|
||||
self.postMessage({ type: 'error', data: { error: err } } satisfies WorkerMessageEvent)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async function extract(text: string | string[], options?: FeatureExtractionPipelineOptions) {
|
||||
const result = await embed(text, options)
|
||||
self.postMessage({ type: 'extractResult', data: { input: { text, options }, output: { data: Array.from(result.data), dims: result.dims } } } satisfies WorkerMessageEvent)
|
||||
}
|
||||
|
||||
self.addEventListener('message', (event: MessageEvent<WorkerMessageEvent>) => {
|
||||
const { type } = event.data
|
||||
|
||||
switch (type) {
|
||||
case 'load':
|
||||
load(event.data.data.modelId, event.data.data.options)
|
||||
break
|
||||
case 'extract':
|
||||
extract(event.data.data.text, event.data.data.options)
|
||||
break
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"lib": [
|
||||
"ESNext",
|
||||
"DOM",
|
||||
"DOM.Iterable",
|
||||
"WebWorker"
|
||||
],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"types": [
|
||||
"vite/client",
|
||||
// @webgpu/types
|
||||
// https://www.npmjs.com/package/@webgpu/types
|
||||
"@webgpu/types"
|
||||
],
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"isolatedModules": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import {
|
||||
defineConfig,
|
||||
presetAttributify,
|
||||
presetIcons,
|
||||
presetTypography,
|
||||
presetWebFonts,
|
||||
presetWind3,
|
||||
transformerDirectives,
|
||||
transformerVariantGroup,
|
||||
} from 'unocss'
|
||||
|
||||
export default defineConfig({
|
||||
presets: [
|
||||
presetWind3(),
|
||||
presetAttributify(),
|
||||
presetTypography(),
|
||||
presetWebFonts({
|
||||
fonts: {
|
||||
sans: 'DM Sans',
|
||||
serif: 'DM Serif Display',
|
||||
mono: 'DM Mono',
|
||||
},
|
||||
}),
|
||||
presetIcons({
|
||||
scale: 1.2,
|
||||
}),
|
||||
],
|
||||
transformers: [
|
||||
transformerDirectives(),
|
||||
transformerVariantGroup(),
|
||||
],
|
||||
safelist: 'prose prose-sm m-auto text-left'.split(' '),
|
||||
})
|
||||
@@ -0,0 +1,13 @@
|
||||
import Vue from '@vitejs/plugin-vue'
|
||||
import Unocss from 'unocss/vite'
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
export default defineConfig({
|
||||
root: 'playground',
|
||||
plugins: [
|
||||
Vue(),
|
||||
// https://github.com/antfu/unocss
|
||||
// see uno.config.ts for config
|
||||
Unocss(),
|
||||
],
|
||||
})
|
||||
@@ -0,0 +1,32 @@
|
||||
import { defineConfig } from 'vitest/config'
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
workspace: [
|
||||
{
|
||||
extends: true,
|
||||
test: {
|
||||
name: 'node',
|
||||
environment: 'node',
|
||||
include: ['**/*.{spec,test}.ts'],
|
||||
exclude: ['**/*.browser.{spec,test}.ts', '**/node_modules/**'],
|
||||
},
|
||||
},
|
||||
{
|
||||
extends: true,
|
||||
test: {
|
||||
name: 'browser',
|
||||
include: ['**/*.browser.{spec,test}.ts'],
|
||||
exclude: ['**/node_modules/**'],
|
||||
browser: {
|
||||
enabled: true,
|
||||
provider: 'playwright',
|
||||
instances: [
|
||||
{ browser: 'chromium' },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "@proj-airi/utils-transformers",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"description": "Utility functions and types for Transformers.js",
|
||||
"author": {
|
||||
"name": "Neko Ayaka",
|
||||
"email": "neko@ayaka.moe",
|
||||
"url": "https://github.com/nekomeowww"
|
||||
},
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/moeru-ai/airi.git",
|
||||
"directory": "packages/utils-transformers"
|
||||
},
|
||||
"exports": {
|
||||
"./types": {
|
||||
"types": "./dist/types/index.d.ts"
|
||||
}
|
||||
},
|
||||
"types": "./dist/types/index.d.ts",
|
||||
"files": [
|
||||
"README.md",
|
||||
"dist",
|
||||
"package.json"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "pnpm run stub",
|
||||
"stub": "unbuild --stub",
|
||||
"build": "unbuild",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@huggingface/transformers": "^3.3.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { AutoModel, pipeline } from '@huggingface/transformers'
|
||||
|
||||
export type DType = Record<string, Exclude<NonNullable<Required<Parameters<typeof pipeline>>[2]['dtype']>, string>[string]>
|
||||
export type Device = Extract<Exclude<NonNullable<Required<Parameters<typeof pipeline>>[2]['device']>, Record<string, any>>, 'webgpu' | 'wasm'>
|
||||
export type PretrainedConfig = NonNullable<Parameters<typeof AutoModel.from_pretrained>[1]>['config']
|
||||
export type PretrainedConfigFrom<T> = T extends { from_pretrained: (...args: any) => any } ? NonNullable<Parameters<T['from_pretrained']>[1]>['config'] : never
|
||||
export type PipelineOptionsFrom<T> = T extends (...args: any) => any ? NonNullable<Parameters<T>[2]> : never
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"lib": [
|
||||
"ESNext"
|
||||
],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"isolatedModules": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
]
|
||||
}
|
||||
Generated
+479
-419
File diff suppressed because it is too large
Load Diff
+11
-7
@@ -6,11 +6,15 @@ packages:
|
||||
- apps/**
|
||||
- '!**/dist/**'
|
||||
catalog:
|
||||
'@xsai/generate-speech': ^0.1.0-beta.7
|
||||
'@xsai/generate-text': ^0.1.0-beta.7
|
||||
'@xsai/generate-transcription': ^0.1.0-beta.7
|
||||
'@xsai/model': ^0.1.0-beta.7
|
||||
'@xsai/generate-speech': ^0.1.0-beta.9
|
||||
'@xsai/generate-text': ^0.1.0-beta.9
|
||||
'@xsai/generate-transcription': ^0.1.0-beta.9
|
||||
'@xsai/model': ^0.1.0-beta.9
|
||||
'@xsai/providers': ^0.1.0-beta.5
|
||||
'@xsai/shared-chat': ^0.1.0-beta.7
|
||||
'@xsai/stream-text': ^0.1.0-beta.7
|
||||
'@xsai/tool': ^0.1.0-beta.7
|
||||
'@xsai/shared-chat': ^0.1.0-beta.9
|
||||
'@xsai/stream-text': ^0.1.0-beta.9
|
||||
'@xsai/tool': ^0.1.0-beta.9
|
||||
'@xsai/utils-chat': ^0.1.0-beta.9
|
||||
'@xsai/embed': ^0.1.0-beta.9
|
||||
'@xsai/shared': ^0.1.0-beta.9
|
||||
'@xsai-ext/shared-providers': ^0.1.0-beta.9
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@guiiai/logg": "^1.0.7",
|
||||
"@proj-airi/server-sdk": "^0.3.3",
|
||||
"@proj-airi/server-sdk": "^0.3.4",
|
||||
"@typeschema/zod": "^0.14.0",
|
||||
"awilix": "^12.0.4",
|
||||
"es-toolkit": "^1.32.0",
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"@xsai/shared-chat": "catalog:",
|
||||
"@xsai/tool": "catalog:",
|
||||
"dotenv": "^16.4.7",
|
||||
"drizzle-orm": "^0.39.3",
|
||||
"drizzle-orm": "^0.40.0",
|
||||
"es-toolkit": "^1.32.0",
|
||||
"grammy": "^1.35.0",
|
||||
"pg": "^8.13.3",
|
||||
|
||||
Reference in New Issue
Block a user