fix(stage-web|stage-tamagotchi): duckdb-wasm import url

This commit is contained in:
Neko Ayaka
2025-04-06 01:58:28 +08:00
parent ef9b29b419
commit 9a11d1b963
14 changed files with 591 additions and 390 deletions
+1 -1
View File
@@ -31,7 +31,7 @@
},
"devDependencies": {
"@iconify-json/solar": "^1.2.2",
"@unocss/reset": "^66.1.0-beta.9",
"@unocss/reset": "^66.1.0-beta.10",
"@vitejs/plugin-vue": "^5.2.3",
"superjson": "^2.2.2",
"unplugin-vue-router": "^0.12.0",
+5 -4
View File
@@ -7,9 +7,10 @@
"author": "LemonNekoGH",
"scripts": {
"typecheck": "vue-tsc --noEmit --composite false",
"start": "electron-vite preview",
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"tauri": "tauri",
"build:unpack": "tauri build --no-bundle"
},
"dependencies": {
@@ -76,7 +77,7 @@
"vaul-vue": "^0.4.1",
"vue": "^3.5.13",
"vue-demi": "^0.14.10",
"vue-i18n": "^11.1.2",
"vue-i18n": "^11.1.3",
"vue-router": "^4.5.0",
"xsschema": "catalog:",
"yauzl": "^3.2.0",
@@ -105,11 +106,11 @@
"@types/nprogress": "^0.2.3",
"@types/three": "^0.175.0",
"@types/yauzl": "^2.10.3",
"@unocss/reset": "^66.1.0-beta.9",
"@unocss/reset": "^66.1.0-beta.10",
"@vitejs/plugin-vue": "^5.2.3",
"@vue-macros/volar": "^3.0.0-beta.7",
"@vueuse/motion": "^3.0.3",
"less": "^4.2.2",
"less": "^4.3.0",
"markdown-it-link-attributes": "^4.0.1",
"unocss-preset-scrollbar": "^3.2.0",
"unplugin-auto-import": "^19.1.2",
+4 -2
View File
@@ -14,12 +14,14 @@ export default defineConfig({
optimizeDeps: {
exclude: [
'@proj-airi/stage-ui/*',
'@proj-airi/drizzle-duckdb-wasm',
'@proj-airi/drizzle-duckdb-wasm/*',
],
},
resolve: {
alias: {
'@proj-airi/stage-ui': resolve(join(import.meta.dirname, '..', '..', 'packages', 'stage-ui', 'dist')),
'@proj-airi/stage-ui/stores': resolve(join(import.meta.dirname, '..', '..', 'packages', 'stage-ui', 'dist', 'stores')),
'@proj-airi/stage-ui': resolve(join(import.meta.dirname, '..', '..', 'packages', 'stage-ui', 'src')),
'@proj-airi/stage-ui/stores': resolve(join(import.meta.dirname, '..', '..', 'packages', 'stage-ui', 'src', 'stores')),
},
},
plugins: [
+3 -3
View File
@@ -86,7 +86,7 @@
"vaul-vue": "^0.4.1",
"vue": "^3.5.13",
"vue-demi": "^0.14.10",
"vue-i18n": "^11.1.2",
"vue-i18n": "^11.1.3",
"vue-router": "^4.5.0",
"xsschema": "catalog:",
"yauzl": "^3.2.0",
@@ -113,12 +113,12 @@
"@types/nprogress": "^0.2.3",
"@types/three": "^0.175.0",
"@types/yauzl": "^2.10.3",
"@unocss/reset": "^66.1.0-beta.9",
"@unocss/reset": "^66.1.0-beta.10",
"@vitejs/plugin-vue": "^5.2.3",
"@vue-macros/volar": "^3.0.0-beta.7",
"@vueuse/motion": "^3.0.3",
"hfup": "^0.5.0",
"less": "^4.2.2",
"less": "^4.3.0",
"markdown-it-link-attributes": "^4.0.1",
"unplugin-auto-import": "^19.1.2",
"unplugin-vue-components": "^28.4.1",
+180
View File
@@ -0,0 +1,180 @@
<script setup lang="ts">
import type { AssistantMessage, Message } from '@xsai/shared-chat'
import { useLocalStorage } from '@vueuse/core'
import { streamText } from '@xsai/stream-text'
import { createWorkflow, workflowEvent } from 'fluere'
import { promiseHandler } from 'fluere/interrupter/promise'
import { withValidation } from 'fluere/middleware/validation'
import { ref, toRaw } from 'vue'
const baseUrl = useLocalStorage('settings/llm/baseUrl', 'https://openrouter.ai/api/v1/')
const apiKey = useLocalStorage('settings/llm/apiKey', '')
const model = useLocalStorage('settings/llm/model', 'openai/gpt-4o-mini')
const sendingMessage = ref('')
const messages = ref<Message[]>([])
const streamingMessage = ref<AssistantMessage>({ role: 'assistant', content: '' })
const loading = ref(false)
const sendingEvent = workflowEvent<void, 'sending'>()
const tokenEvent = workflowEvent<string, 'token'>()
const textEvent = workflowEvent<string, 'text'>()
const sentenceEvent = workflowEvent<string, 'sentence'>()
const doneEvent = workflowEvent<void, 'done'>()
async function handleChatSendMessage() {
loading.value = true
try {
const streamWorkflow = withValidation(createWorkflow(), [
[[sendingEvent], [tokenEvent, doneEvent]],
[[tokenEvent], [textEvent]],
[[textEvent], [sentenceEvent]],
])
streamWorkflow.handle([sendingEvent], async (sendEvent) => {
streamingMessage.value = { role: 'assistant', content: '' }
messages.value.push({ role: 'user', content: sendingMessage.value })
messages.value.push(streamingMessage.value)
const response = await streamText({
baseURL: baseUrl.value,
apiKey: apiKey.value,
model: model.value,
messages: messages.value.slice(0, messages.value.length - 1).map(msg => toRaw(msg)),
})
for await (const chunk of response.chunkStream)
sendEvent(tokenEvent.with(chunk.choices[0].delta.content || ''))
return doneEvent.with()
})
streamWorkflow.handle([tokenEvent], async (_sendEvent, token) => {
if (!streamingMessage.value.content)
streamingMessage.value.content = token.data
else
streamingMessage.value.content += token.data
})
await promiseHandler(streamWorkflow, sendingEvent.with(), doneEvent)
}
catch (err) {
console.error(err)
}
finally {
loading.value = false
}
}
// function useMessageTerminationWorkflow(parentWorkflow: WithValidationWorkflow<[[[typeof textEvent], [typeof sentenceEvent]]]>) {
// let processed = ''
// parentWorkflow.handle([textEvent], async (sendEvent, text) => {
// const endMarker = /[.?!]/
// processed += text.data
// while (processed) {
// const endMarkerExecArray = endMarker.exec(processed)
// if (!endMarkerExecArray || typeof endMarkerExecArray.index === 'undefined')
// break
// const before = processed.slice(0, endMarkerExecArray.index + 1)
// const after = processed.slice(endMarkerExecArray.index + 1)
// sendEvent(sentenceEvent.with(before))
// processed = after
// }
// })
// parentWorkflow.handle([doneEvent], async () => {
// const { sendEvent } = getContext()
// const content = processed.trim()
// if (content)
// sendEvent(sentenceEvent.with(content))
// processed = ''
// })
// }
</script>
<template>
<div flex flex-col gap-2>
<!-- <h2 text-xl>
Storage
</h2> -->
<div flex="~ col" gap-2>
<div flex flex-col gap-2>
<div>
<span text-neutral-500 dark:text-neutral-400>LLM</span>
</div>
<div grid grid-cols-2 gap-2>
<label flex items-center gap-2>
<span text-nowrap>
Base URL
</span>
<input
v-model="baseUrl"
border="focus:primary-100 dark:focus:primary-400/50 2 solid neutral-200 dark:neutral-800"
transition="all duration-200 ease-in-out" text="disabled:neutral-400 dark:disabled:neutral-600"
cursor="disabled:not-allowed" w-full rounded-lg px-2 py-1 text-nowrap text-sm outline-none shadow="sm"
bg="neutral-100 dark:neutral-800 focus:neutral-50 dark:focus:neutral-900"
>
</label>
<label flex items-center gap-2>
<span text-nowrap>
API Key
</span>
<input
v-model="apiKey"
type="password"
border="focus:primary-100 dark:focus:primary-400/50 2 solid neutral-200 dark:neutral-800"
transition="all duration-200 ease-in-out" text="disabled:neutral-400 dark:disabled:neutral-600"
cursor="disabled:not-allowed" w-full rounded-lg px-2 py-1 text-nowrap text-sm outline-none shadow="sm"
bg="neutral-100 dark:neutral-800 focus:neutral-50 dark:focus:neutral-900"
>
</label>
<label flex items-center gap-2>
<span text-nowrap>
Model
</span>
<input
v-model="model"
border="focus:primary-100 dark:focus:primary-400/50 2 solid neutral-200 dark:neutral-800"
transition="all duration-200 ease-in-out" text="disabled:neutral-400 dark:disabled:neutral-600"
cursor="disabled:not-allowed" w-full rounded-lg px-2 py-1 text-nowrap text-sm outline-none shadow="sm"
bg="neutral-100 dark:neutral-800 focus:neutral-50 dark:focus:neutral-900"
>
</label>
</div>
</div>
<div>
<textarea
v-model="sendingMessage"
border="focus:primary-100 dark:focus:primary-400/50 2 solid neutral-200 dark:neutral-800"
transition="all duration-200 ease-in-out" text="disabled:neutral-400 dark:disabled:neutral-600"
cursor="disabled:not-allowed" w-full rounded-lg px-2 py-1 text-nowrap text-sm outline-none shadow="sm"
bg="neutral-100 dark:neutral-800 focus:neutral-50 dark:focus:neutral-900"
/>
</div>
<button rounded-lg bg="blue-100 dark:blue-900" px-4 py-2 @click="handleChatSendMessage">
Send
</button>
<div>
<div v-for="(message, index) of messages" :key="index">
<div v-if="message.role === 'user'">
<span>
{{ message.content }}
</span>
</div>
<div v-if="message.role === 'assistant'">
<span>
{{ message.content }}
</span>
</div>
</div>
</div>
</div>
</div>
</template>
+2
View File
@@ -24,6 +24,8 @@ export default defineConfig({
optimizeDeps: {
exclude: [
'@proj-airi/stage-ui/*',
'@proj-airi/drizzle-duckdb-wasm',
'@proj-airi/drizzle-duckdb-wasm/*',
'public/assets/*',
'@framework/live2dcubismframework',
'@framework/math/cubismmatrix44',
+1 -1
View File
@@ -12,7 +12,7 @@
},
"devDependencies": {
"98.css": "^0.1.20",
"@astrojs/starlight": "^0.32.5",
"@astrojs/starlight": "^0.32.6",
"@fontsource/fusion-pixel-12px-proportional-sc": "^5.2.5",
"@fontsource/quicksand": "^5.2.6",
"astro": "^5.6.1",
+3 -3
View File
@@ -36,8 +36,8 @@
"@antfu/ni": "^24.3.0",
"@cspell/dict-ru_ru": "^2.2.4",
"@types/node": "^22.14.0",
"@unocss/eslint-config": "^66.1.0-beta.9",
"@unocss/eslint-plugin": "^66.1.0-beta.9",
"@unocss/eslint-config": "^66.1.0-beta.10",
"@unocss/eslint-plugin": "^66.1.0-beta.10",
"@vitest/coverage-v8": "3.0.5",
"bumpp": "^10.1.0",
"changelogithub": "^13.13.0",
@@ -51,7 +51,7 @@
"taze": "^19.0.4",
"typescript": "~5.8.3",
"unbuild": "3.0.0-rc.11",
"unocss": "^66.1.0-beta.9",
"unocss": "^66.1.0-beta.10",
"vite": "^6.2.5",
"vite-plugin-inspect": "^11.0.0",
"vitest": "^3.1.1"
+1 -1
View File
@@ -62,7 +62,7 @@
},
"devDependencies": {
"@iconify-json/solar": "^1.2.2",
"@unocss/reset": "^66.1.0-beta.9",
"@unocss/reset": "^66.1.0-beta.10",
"@vitejs/plugin-vue": "^5.2.3",
"@vueuse/core": "^13.0.0",
"vite": "^6.2.5",
+2 -2
View File
@@ -72,11 +72,11 @@
"@histoire/plugin-vue": "1.0.0-alpha.2",
"@proj-airi/provider-transformers": "workspace:^",
"@proj-airi/utils-transformers": "workspace:^",
"@unocss/reset": "^66.1.0-beta.9",
"@unocss/reset": "^66.1.0-beta.10",
"@vitejs/plugin-vue": "^5.2.3",
"@xsai/embed": "catalog:",
"histoire": "1.0.0-alpha.2",
"unocss": "^66.1.0-beta.9",
"unocss": "^66.1.0-beta.10",
"vite": "^6.2.5"
}
}
@@ -6,6 +6,7 @@ import type { SpeechProviderWithExtraOptions } from '@xsai-ext/shared-providers'
import type { Emotion } from '../../constants/emotions'
import { drizzle } from '@proj-airi/drizzle-duckdb-wasm'
import { getImportUrlBundles } from '@proj-airi/drizzle-duckdb-wasm/bundles/import-url-browser'
// import { createTransformers } from '@proj-airi/provider-transformers'
// import embedWorkerURL from '@proj-airi/provider-transformers/worker?worker&url'
// import { embed } from '@xsai/embed'
@@ -235,7 +236,7 @@ onMounted(() => {
})
onMounted(async () => {
db.value = drizzle('duckdb-wasm://?bundles=import-url')
db.value = drizzle({ connection: { bundles: getImportUrlBundles() } })
await db.value.execute(sql`CREATE TABLE memory_test (vec FLOAT[768]);`)
})
</script>
+1 -1
View File
@@ -34,7 +34,7 @@
"vue": "^3.5.13"
},
"devDependencies": {
"@unocss/reset": "^66.1.0-beta.9",
"@unocss/reset": "^66.1.0-beta.10",
"@vitejs/plugin-vue": "^5.2.3",
"unplugin-vue-router": "^0.12.0",
"vite-plugin-vue-devtools": "^7.7.2",
+385 -370
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -7,7 +7,7 @@ packages:
- '!**/dist/**'
catalog:
'@proj-airi/drizzle-duckdb-wasm': ^0.4.20
'@proj-airi/drizzle-duckdb-wasm': ^0.4.21
'@xsai/shared': &xsai ^0.2.0-beta.3
'@xsai-ext/providers-cloud': *xsai
'@xsai-ext/providers-local': *xsai