From d70d42e5fedbc20764843c7f9d426f87f53147be Mon Sep 17 00:00:00 2001 From: Blinko Date: Thu, 28 Nov 2024 15:02:37 +0800 Subject: [PATCH] feat: support ollama --- index.html | 14 +++ package.json | 3 +- pnpm-lock.yaml | 38 +++++- src/components/BlinkoSettings/AiSetting.tsx | 118 ++++++++++--------- src/server/plugins/ai.ts | 44 ++++--- src/server/plugins/ai/aiModelFactory.ts | 20 +++- src/server/plugins/ai/ollamaModelProvider.ts | 24 ++++ src/server/plugins/ai/openAIModelProvider.ts | 23 ++-- src/server/routers/ai.ts | 12 +- src/store/aiStore.tsx | 33 +++++- 10 files changed, 232 insertions(+), 97 deletions(-) create mode 100644 index.html create mode 100644 src/server/plugins/ai/ollamaModelProvider.ts diff --git a/index.html b/index.html new file mode 100644 index 00000000..9eb98906 --- /dev/null +++ b/index.html @@ -0,0 +1,14 @@ +Google



 

進階搜尋

Google 透過以下語言提供: 简体中文 English

© 2024 - 私隱權政策 - 條款

\ No newline at end of file diff --git a/package.json b/package.json index 79291bc2..5c77d6a8 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "db-seed": "prisma db seed", "build-seed": "tsc -p tsconfig.seed.json", "db-reset": "prisma migrate dev reset", - "dev": "next dev -p 1111", + "dev": "next dev -p 1112", "dev:https": "ngrok http 1111", "build": "set NODE_ENV=production & next build", "vercel-build": "prisma generate && prisma migrate deploy && prisma db seed && next build", @@ -37,6 +37,7 @@ "@iconify/react": "^5.0.2", "@langchain/community": "^0.3.6", "@langchain/core": "^0.3.13", + "@langchain/ollama": "^0.1.2", "@langchain/openai": "^0.3.10", "@langchain/pinecone": "^0.1.1", "@langchain/textsplitters": "^0.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3d5ecb2b..30d75700 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -28,10 +28,13 @@ importers: version: 5.0.2(react@18.3.1) '@langchain/community': specifier: ^0.3.6 - version: 0.3.6(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.693.0)(@aws-sdk/credential-provider-node@3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0))(@langchain/core@0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8)))(@pinecone-database/pinecone@3.0.3)(@smithy/util-utf8@2.3.0)(axios@1.7.7)(cheerio@1.0.0)(encoding@0.1.13)(faiss-node@0.5.1)(handlebars@4.7.8)(ignore@5.3.2)(jsdom@21.1.2)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.8.0)(openai@4.68.0(encoding@0.1.13)(zod@3.23.8))(pdf-parse@1.1.1)(pg@8.13.0)(typeorm@0.3.20(pg@8.13.0)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@20.2.5)(typescript@5.6.3)))(ws@8.18.0) + version: 0.3.6(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.693.0)(@aws-sdk/credential-provider-node@3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0))(@langchain/core@0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8)))(@langchain/ollama@0.1.2(@langchain/core@0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8))))(@pinecone-database/pinecone@3.0.3)(@smithy/util-utf8@2.3.0)(axios@1.7.7)(cheerio@1.0.0)(encoding@0.1.13)(faiss-node@0.5.1)(handlebars@4.7.8)(ignore@5.3.2)(jsdom@21.1.2)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.8.0)(openai@4.68.0(encoding@0.1.13)(zod@3.23.8))(pdf-parse@1.1.1)(pg@8.13.0)(typeorm@0.3.20(pg@8.13.0)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@20.2.5)(typescript@5.6.3)))(ws@8.18.0) '@langchain/core': specifier: ^0.3.13 version: 0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8)) + '@langchain/ollama': + specifier: ^0.1.2 + version: 0.1.2(@langchain/core@0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8))) '@langchain/openai': specifier: ^0.3.10 version: 0.3.10(@langchain/core@0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13) @@ -148,7 +151,7 @@ importers: version: 0.16.11 langchain: specifier: ^0.3.2 - version: 0.3.2(@langchain/core@0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.7)(cheerio@1.0.0)(encoding@0.1.13)(handlebars@4.7.8)(openai@4.68.0(encoding@0.1.13)(zod@3.23.8))(typeorm@0.3.20(pg@8.13.0)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@20.2.5)(typescript@5.6.3))) + version: 0.3.2(@langchain/core@0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8)))(@langchain/ollama@0.1.2(@langchain/core@0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8))))(axios@1.7.7)(cheerio@1.0.0)(encoding@0.1.13)(handlebars@4.7.8)(openai@4.68.0(encoding@0.1.13)(zod@3.23.8))(typeorm@0.3.20(pg@8.13.0)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@20.2.5)(typescript@5.6.3))) lexical: specifier: 0.17.1 version: 0.17.1 @@ -1945,6 +1948,12 @@ packages: resolution: {integrity: sha512-sHDlwyHhgeaYC+wfORrWO7sXxD6/GDtZZ5mqjY48YMwB58cVv8hTs8goR/9EwXapYt8fQi2uXTGUV87bHzvdZQ==} engines: {node: '>=18'} + '@langchain/ollama@0.1.2': + resolution: {integrity: sha512-WCeogCFjdWf6jGwLt12cxkSpm5eVamv43b48DIlbJ4np9vChwVlZZB6FU7uEXNrJ9c0dsoa6877hJ5mYHdbJvw==} + engines: {node: '>=18'} + peerDependencies: + '@langchain/core': ^0.3.13 + '@langchain/openai@0.3.10': resolution: {integrity: sha512-kKapg816ZMNscCKdfo7vQiugtfJPIt14LPHoOsDBQxYRK5fSLUHQ/5eb2iujLipymjMeHlataOGzObSVEwzEmw==} engines: {node: '>=18'} @@ -7416,6 +7425,9 @@ packages: resolution: {integrity: sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==} engines: {node: ^10.13.0 || >=12.0.0} + ollama@0.5.10: + resolution: {integrity: sha512-M5O4u6S6yZeeKFrKA7ZfGGLjK54otOVGPrOUc3N64zSTpz9J+x/nh93dmD6Py7YLgXzq9I6Nq+PDDoaqJuV3LQ==} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -9654,6 +9666,9 @@ packages: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} + whatwg-fetch@3.6.20: + resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} + whatwg-mimetype@3.0.0: resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} engines: {node: '>=12'} @@ -11769,7 +11784,7 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - '@langchain/community@0.3.6(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.693.0)(@aws-sdk/credential-provider-node@3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0))(@langchain/core@0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8)))(@pinecone-database/pinecone@3.0.3)(@smithy/util-utf8@2.3.0)(axios@1.7.7)(cheerio@1.0.0)(encoding@0.1.13)(faiss-node@0.5.1)(handlebars@4.7.8)(ignore@5.3.2)(jsdom@21.1.2)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.8.0)(openai@4.68.0(encoding@0.1.13)(zod@3.23.8))(pdf-parse@1.1.1)(pg@8.13.0)(typeorm@0.3.20(pg@8.13.0)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@20.2.5)(typescript@5.6.3)))(ws@8.18.0)': + '@langchain/community@0.3.6(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.693.0)(@aws-sdk/credential-provider-node@3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0))(@langchain/core@0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8)))(@langchain/ollama@0.1.2(@langchain/core@0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8))))(@pinecone-database/pinecone@3.0.3)(@smithy/util-utf8@2.3.0)(axios@1.7.7)(cheerio@1.0.0)(encoding@0.1.13)(faiss-node@0.5.1)(handlebars@4.7.8)(ignore@5.3.2)(jsdom@21.1.2)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.8.0)(openai@4.68.0(encoding@0.1.13)(zod@3.23.8))(pdf-parse@1.1.1)(pg@8.13.0)(typeorm@0.3.20(pg@8.13.0)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@20.2.5)(typescript@5.6.3)))(ws@8.18.0)': dependencies: '@langchain/core': 0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8)) '@langchain/openai': 0.3.10(@langchain/core@0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13) @@ -11777,7 +11792,7 @@ snapshots: expr-eval: 2.0.2 flat: 5.0.2 js-yaml: 4.1.0 - langchain: 0.3.2(@langchain/core@0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.7)(cheerio@1.0.0)(encoding@0.1.13)(handlebars@4.7.8)(openai@4.68.0(encoding@0.1.13)(zod@3.23.8))(typeorm@0.3.20(pg@8.13.0)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@20.2.5)(typescript@5.6.3))) + langchain: 0.3.2(@langchain/core@0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8)))(@langchain/ollama@0.1.2(@langchain/core@0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8))))(axios@1.7.7)(cheerio@1.0.0)(encoding@0.1.13)(handlebars@4.7.8)(openai@4.68.0(encoding@0.1.13)(zod@3.23.8))(typeorm@0.3.20(pg@8.13.0)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@20.2.5)(typescript@5.6.3))) langsmith: 0.1.66(openai@4.68.0(encoding@0.1.13)(zod@3.23.8)) uuid: 10.0.0 zod: 3.23.8 @@ -11830,6 +11845,12 @@ snapshots: transitivePeerDependencies: - openai + '@langchain/ollama@0.1.2(@langchain/core@0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8)))': + dependencies: + '@langchain/core': 0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8)) + ollama: 0.5.10 + uuid: 10.0.0 + '@langchain/openai@0.3.10(@langchain/core@0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13)': dependencies: '@langchain/core': 0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8)) @@ -18100,7 +18121,7 @@ snapshots: kleur@3.0.3: {} - langchain@0.3.2(@langchain/core@0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.7)(cheerio@1.0.0)(encoding@0.1.13)(handlebars@4.7.8)(openai@4.68.0(encoding@0.1.13)(zod@3.23.8))(typeorm@0.3.20(pg@8.13.0)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@20.2.5)(typescript@5.6.3))): + langchain@0.3.2(@langchain/core@0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8)))(@langchain/ollama@0.1.2(@langchain/core@0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8))))(axios@1.7.7)(cheerio@1.0.0)(encoding@0.1.13)(handlebars@4.7.8)(openai@4.68.0(encoding@0.1.13)(zod@3.23.8))(typeorm@0.3.20(pg@8.13.0)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@20.2.5)(typescript@5.6.3))): dependencies: '@langchain/core': 0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8)) '@langchain/openai': 0.3.10(@langchain/core@0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13) @@ -18116,6 +18137,7 @@ snapshots: zod: 3.23.8 zod-to-json-schema: 3.23.3(zod@3.23.8) optionalDependencies: + '@langchain/ollama': 0.1.2(@langchain/core@0.3.13(openai@4.68.0(encoding@0.1.13)(zod@3.23.8))) axios: 1.7.7 cheerio: 1.0.0 handlebars: 4.7.8 @@ -19218,6 +19240,10 @@ snapshots: oidc-token-hash@5.0.3: {} + ollama@0.5.10: + dependencies: + whatwg-fetch: 3.6.20 + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -21717,6 +21743,8 @@ snapshots: iconv-lite: 0.6.3 optional: true + whatwg-fetch@3.6.20: {} + whatwg-mimetype@3.0.0: optional: true diff --git a/src/components/BlinkoSettings/AiSetting.tsx b/src/components/BlinkoSettings/AiSetting.tsx index 2ab742aa..9b5648b9 100644 --- a/src/components/BlinkoSettings/AiSetting.tsx +++ b/src/components/BlinkoSettings/AiSetting.tsx @@ -1,5 +1,5 @@ import { observer } from "mobx-react-lite"; -import { Button, Card, Code, Input, Select, SelectItem, Switch, Tooltip } from "@nextui-org/react"; +import { Autocomplete, AutocompleteItem, Button, Card, Code, Input, Select, SelectItem, Switch, Tooltip } from "@nextui-org/react"; import { RootStore } from "@/store"; import { BlinkoStore } from "@/store/blinkoStore"; import { PromiseCall } from "@/store/standard/PromiseState"; @@ -54,11 +54,12 @@ export const AiSetting = observer(() => { } /> @@ -76,14 +77,15 @@ export const AiSetting = observer(() => { ai.modelSelect[blinko.config.value?.aiModelProvider!] && {t('ai-model')}} rightContent={ - } /> + + } /> } {ai.embeddingSelect[blinko.config.value?.aiModelProvider!] && ( @@ -107,14 +110,15 @@ export const AiSetting = observer(() => { } />} rightContent={
- +
} /> )} - -
API Key
-
{t('user-custom-openai-api-key')}
- } - rightContent={ - { store.apiKey = e.target.value }} - onBlur={e => { - PromiseCall(api.config.update.mutate({ - key: 'aiApiKey', - value: store.apiKey - })) - }} - endContent={ - - } - type={store.isVisible ? "text" : "password"} - /> - } /> + { + blinko.config.value?.aiModelProvider != 'Ollama' && + +
API Key
+
{t('user-custom-openai-api-key')}
+ } + rightContent={ + { store.apiKey = e.target.value }} + onBlur={e => { + PromiseCall(api.config.update.mutate({ + key: 'aiApiKey', + value: store.apiKey + })) + }} + endContent={ + + } + type={store.isVisible ? "text" : "password"} + /> + } /> + } + diff --git a/src/server/plugins/ai.ts b/src/server/plugins/ai.ts index 2ec03d5c..206dd602 100644 --- a/src/server/plugins/ai.ts +++ b/src/server/plugins/ai.ts @@ -2,6 +2,7 @@ import { _ } from '@/lib/lodash'; import "pdf-parse"; import { ChatOpenAI, ClientOptions, OpenAIEmbeddings, } from "@langchain/openai"; import path from 'path'; +import fs from 'fs'; import type { Document } from "@langchain/core/documents"; import { AIMessage, HumanMessage, SystemMessage } from '@langchain/core/messages'; import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts"; @@ -65,6 +66,7 @@ export class AiService { await VectorStore.delete({ ids: [`${id}-${index}`] }) await VectorStore.save(FaissStorePath) } catch (error) { + console.log('error', error) break; } } @@ -85,11 +87,9 @@ export class AiService { try { const { VectorStore, MarkdownSplitter } = await AiModelFactory.GetProvider() const chunks = await MarkdownSplitter.splitText(content); - if (type == 'update') { await AiService.embeddingDeleteAll(id, VectorStore) } - const documents: Document[] = chunks.map((chunk, index) => { return { pageContent: chunk, @@ -117,7 +117,7 @@ export class AiService { await VectorStore.save(FaissStorePath) return { ok: true } } catch (error) { - return { ok: false, error } + return { ok: false, error: error?.message } } } @@ -186,11 +186,20 @@ export class AiService { static async similaritySearch({ question }: { question: string }) { const { VectorStore } = await AiModelFactory.GetProvider() - const result = await VectorStore.similaritySearch(question, 2); - return result + const results = await VectorStore.similaritySearchWithScore(question, 2); + // console.log('similaritySearch with scores:', results) + const DISTANCE_THRESHOLD = 1.5; + const filteredResults = results + .filter(([doc, distance]) => distance < DISTANCE_THRESHOLD) + .map(([doc]) => doc); + return filteredResults; } static async *rebuildEmbeddingIndex({ force = false }: { force?: boolean }): AsyncGenerator { + if(force){ + const faissPath = path.join(process.cwd(), FAISS_PATH) + fs.rmSync(faissPath, { recursive: true, force: true }) + } const notes = await prisma.notes.findMany({ include: { attachments: true @@ -217,11 +226,25 @@ export class AiService { continue; } if (note?.content != '') { - await AiService.embeddingUpsert({ + const { ok, error } = await AiService.embeddingUpsert({ id: note?.id, content: note?.content, type: 'update' as const }); + if (ok) { + yield { + type: 'success' as const, + content: note?.content.slice(0, 30) ?? '', + progress: { current, total } + }; + } else { + yield { + type: 'error' as const, + content: note?.content.slice(0, 30) ?? '', + error, + progress: { current, total } + }; + } } //@ts-ignore if (note?.attachments) { @@ -247,15 +270,8 @@ export class AiService { } } } - if (note?.content != '') { - yield { - type: 'success' as const, - content: note?.content.slice(0, 30) ?? '', - progress: { current, total } - }; - } + } catch (error) { - console.error('rebuild index error->', error); yield { type: 'error' as const, content: note.content.slice(0, 30), diff --git a/src/server/plugins/ai/aiModelFactory.ts b/src/server/plugins/ai/aiModelFactory.ts index bb8c8a67..6b910b59 100644 --- a/src/server/plugins/ai/aiModelFactory.ts +++ b/src/server/plugins/ai/aiModelFactory.ts @@ -5,6 +5,7 @@ import { BaseChatModel } from "@langchain/core/language_models/chat_models" import { Embeddings } from "@langchain/core/embeddings" import { OpenAIModelProvider } from "./openAIModelProvider" import { getGlobalConfig } from "@/server/routers/config" +import { OllamaModelProvider } from "./ollamaModelProvider" export class AiModelFactory { @@ -23,9 +24,11 @@ export class AiModelFactory { } static async GetProvider() { + console.log('GetProvider', ) const globalConfig = await AiModelFactory.ValidConfig() if (globalConfig.aiModelProvider == 'OpenAI') { const provider = new OpenAIModelProvider({ globalConfig }) + console.log('provider', provider) return { LLM: provider.LLM(), VectorStore: await provider.VectorStore(), @@ -34,13 +37,18 @@ export class AiModelFactory { TokenTextSplitter: provider.TokenTextSplitter() } } - return { - LLM: null as unknown as BaseChatModel, - VectorStore: null as unknown as FaissStore, - Embeddings: null as unknown as Embeddings, - MarkdownSplitter: null as unknown as MarkdownTextSplitter, - TokenTextSplitter: null as unknown as TokenTextSplitter + + if (globalConfig.aiModelProvider == 'Ollama') { + const provider = new OllamaModelProvider({ globalConfig }) + return { + LLM: provider.LLM(), + VectorStore: await provider.VectorStore(), + Embeddings: provider.Embeddings(), + MarkdownSplitter: provider.MarkdownSplitter(), + TokenTextSplitter: provider.TokenTextSplitter() + } } + throw new Error('not support other loader') } static async GetAudioLoader(audioPath: string) { diff --git a/src/server/plugins/ai/ollamaModelProvider.ts b/src/server/plugins/ai/ollamaModelProvider.ts new file mode 100644 index 00000000..6f196587 --- /dev/null +++ b/src/server/plugins/ai/ollamaModelProvider.ts @@ -0,0 +1,24 @@ +import { ChatOllama } from "@langchain/ollama"; +import { OllamaEmbeddings } from "@langchain/ollama"; +import { AiBaseModelPrivider } from './openAIModelProvider'; +import { BufferLoader } from "langchain/document_loaders/fs/buffer"; + +export class OllamaModelProvider extends AiBaseModelPrivider { + LLM() { + return new ChatOllama({ + baseUrl: this.globalConfig.aiApiEndpoint, + model: this.globalConfig.aiModel ?? 'llama2' + }); + } + + Embeddings() { + return new OllamaEmbeddings({ + model: this.globalConfig.embeddingModel ?? "mxbai-embed-large", //default model + baseUrl: this.globalConfig.aiApiEndpoint,// "http://localhost:11434" + }); + } + + public AudioLoader(audioPath): BufferLoader { + return null as unknown as BufferLoader; + } +} diff --git a/src/server/plugins/ai/openAIModelProvider.ts b/src/server/plugins/ai/openAIModelProvider.ts index 46f694d8..143c9ba2 100644 --- a/src/server/plugins/ai/openAIModelProvider.ts +++ b/src/server/plugins/ai/openAIModelProvider.ts @@ -42,14 +42,21 @@ export abstract class AiBaseModelPrivider { this.Embeddings() ); } catch (error) { - const VectorStore = new FaissStore(this.Embeddings(), {}); - const documents = [{ - pageContent: "init faiss store", - metadata: { id: '0' }, - }]; - await VectorStore.addDocuments(documents, { ids: ["0"] }); - await VectorStore.save(FaissStorePath) - return VectorStore + try { + console.log(this.globalConfig) + const VectorStore = new FaissStore(this.Embeddings(), {}); + const documents = [{ + pageContent: "init faiss store", + metadata: { id: '0' }, + }]; + console.log('init faiss store', documents) + await VectorStore.addDocuments(documents, { ids: ["0"] }); + await VectorStore.save(FaissStorePath) + return VectorStore + } catch (error) { + console.log('VectorStore error', error) + throw error + } } } diff --git a/src/server/routers/ai.ts b/src/server/routers/ai.ts index bc592ea4..032a398d 100644 --- a/src/server/routers/ai.ts +++ b/src/server/routers/ai.ts @@ -14,12 +14,14 @@ export const aiRouter = router({ })) .mutation(async ({ input }) => { const { id, content, type } = input - try { - const res = await AiService.embeddingUpsert({ id, content, type }) - return res - } catch (error) { - return { ok: false, msg: error?.message } + const { ok, error } = await AiService.embeddingUpsert({ id, content, type }) + if (!ok) { + throw new TRPCError({ + code: 'INTERNAL_SERVER_ERROR', + message: error + }) } + return { ok } }), embeddingInsertAttachments: authProcedure .input(z.object({ diff --git a/src/store/aiStore.tsx b/src/store/aiStore.tsx index 9799e63b..ed3afdbe 100644 --- a/src/store/aiStore.tsx +++ b/src/store/aiStore.tsx @@ -15,6 +15,7 @@ import { DialogStore } from './module/Dialog'; import { CheckboxGroup } from '@nextui-org/react'; import { AiTag } from '@/components/BlinkoAi/aiTag'; import i18n from '@/lib/i18n'; +import { Icon } from '@iconify/react'; type Chat = { content: string @@ -35,10 +36,16 @@ export class AiStore implements Store { content: "", notes: [] as Note[] } - modelProviderSelect: { label: string, value: GlobalConfig['aiModelProvider'] }[] = [ + modelProviderSelect: { label: string, value: GlobalConfig['aiModelProvider'], icon: React.ReactNode }[] = [ { label: "OpenAI", - value: "OpenAI" + value: "OpenAI", + icon: + }, + { + label: "Ollama", + value: "Ollama", + icon: } ] @@ -60,10 +67,16 @@ export class AiStore implements Store { label: "gpt-4o-mini", value: "gpt-4o-mini" } + ], + Ollama: [ + { + label: "llama3.2", + value: "llama3.2" + } ] } - embeddingSelect: Record> = { + embeddingSelect: Record> = { OpenAI: [ { label: "text-embedding-3-small", @@ -77,6 +90,20 @@ export class AiStore implements Store { label: "text-embedding-ada-002", value: "text-embedding-ada-002" } + ], + Ollama: [ + { + label: "mxbai-embed-large", + value: "mxbai-embed-large" + }, + { + label: "nomic-embed-text", + value: "nomic-embed-text" + }, + { + label: "bge-large-en", + value: "bge-large-en" + } ] }