From 38c6f1e64c364d123cb36e6f7e411b40839bbf11 Mon Sep 17 00:00:00 2001 From: mayneyao Date: Fri, 8 Nov 2024 13:40:53 +0800 Subject: [PATCH 01/20] feat(ai): backend api --- apps/nestjs-backend/package.json | 2 ++ apps/nestjs-backend/src/app.module.ts | 2 ++ .../src/features/ai/ai.controller.ts | 13 +++++++++++ .../src/features/ai/ai.module.ts | 11 +++++++++ .../src/features/ai/ai.service.ts | 23 +++++++++++++++++++ 5 files changed, 51 insertions(+) create mode 100644 apps/nestjs-backend/src/features/ai/ai.controller.ts create mode 100644 apps/nestjs-backend/src/features/ai/ai.module.ts create mode 100644 apps/nestjs-backend/src/features/ai/ai.service.ts diff --git a/apps/nestjs-backend/package.json b/apps/nestjs-backend/package.json index 9842539ef3..8b1bdaa0d4 100644 --- a/apps/nestjs-backend/package.json +++ b/apps/nestjs-backend/package.json @@ -109,6 +109,7 @@ "webpack": "5.91.0" }, "dependencies": { + "@ai-sdk/openai": "0.0.72", "@aws-sdk/client-s3": "3.609.0", "@aws-sdk/s3-request-presigner": "3.609.0", "@keyv/redis": "2.8.4", @@ -144,6 +145,7 @@ "@teable/openapi": "workspace:^", "@teamwork/websocket-json-stream": "2.0.0", "@types/papaparse": "5.3.14", + "ai": "3.4.33", "ajv": "8.12.0", "axios": "1.7.7", "bcrypt": "5.1.1", diff --git a/apps/nestjs-backend/src/app.module.ts b/apps/nestjs-backend/src/app.module.ts index 258940acbe..5c7402241f 100644 --- a/apps/nestjs-backend/src/app.module.ts +++ b/apps/nestjs-backend/src/app.module.ts @@ -35,6 +35,7 @@ import { GlobalModule } from './global/global.module'; import { InitBootstrapProvider } from './global/init-bootstrap.provider'; import { LoggerModule } from './logger/logger.module'; import { WsModule } from './ws/ws.module'; +import { AiModule } from './features/ai/ai.module'; export const appModules = { imports: [ @@ -66,6 +67,7 @@ export const appModules = { PluginModule, DashboardModule, CommentOpenApiModule, + AiModule, ], providers: [InitBootstrapProvider], }; diff --git a/apps/nestjs-backend/src/features/ai/ai.controller.ts b/apps/nestjs-backend/src/features/ai/ai.controller.ts new file mode 100644 index 0000000000..1d11c157ea --- /dev/null +++ b/apps/nestjs-backend/src/features/ai/ai.controller.ts @@ -0,0 +1,13 @@ +import { Body, Controller, Post, Res } from '@nestjs/common'; +import { Response } from 'express'; +import { AiService } from './ai.service'; + +@Controller('api/ai') +export class AiController { + constructor(private readonly aiService: AiService) {} + @Post() + async generate(@Body('prompt') prompt: string, @Res() res: Response) { + const result = await this.aiService.generate(prompt); + result.pipeTextStreamToResponse(res); + } +} diff --git a/apps/nestjs-backend/src/features/ai/ai.module.ts b/apps/nestjs-backend/src/features/ai/ai.module.ts new file mode 100644 index 0000000000..ab10fa97a9 --- /dev/null +++ b/apps/nestjs-backend/src/features/ai/ai.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { AiController } from './ai.controller'; +import { ConfigModule } from '@nestjs/config'; +import { AiService } from './ai.service'; + +@Module({ + imports: [ConfigModule], + controllers: [AiController], + providers: [AiService], +}) +export class AiModule {} diff --git a/apps/nestjs-backend/src/features/ai/ai.service.ts b/apps/nestjs-backend/src/features/ai/ai.service.ts new file mode 100644 index 0000000000..ce936ca65e --- /dev/null +++ b/apps/nestjs-backend/src/features/ai/ai.service.ts @@ -0,0 +1,23 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { createOpenAI } from '@ai-sdk/openai'; +import { streamText } from 'ai'; + +@Injectable() +export class AiService { + constructor(private readonly configService: ConfigService) {} + + async generate(prompt: string) { + const openAIBaseUrl = this.configService.get('OPENAI_BASE_URL'); + const openaiApiKey = this.configService.get('OPENAI_API_KEY'); + const openai = createOpenAI({ + baseURL: openAIBaseUrl, + apiKey: openaiApiKey, + }); + const result = await streamText({ + model: openai('gpt-4o-mini'), + prompt: prompt, + }); + return result; + } +} From f1d1b5ce58613ca9e45e04ee1ec3d779cfcdb8dd Mon Sep 17 00:00:00 2001 From: mayneyao Date: Fri, 8 Nov 2024 14:42:01 +0800 Subject: [PATCH 02/20] feat(ai): generate formula with AI --- apps/nextjs-app/.env.example | 5 + apps/nextjs-app/src/styles/global.css | 8 + packages/common-i18n/src/locales/en/sdk.json | 3 +- .../comment/comment-editor/plate-ui/icons.tsx | 32 ++ .../src/components/editor/formula/Editor.tsx | 48 ++- .../editor/formula/components/CodeEditor.tsx | 45 ++- .../editor/formula/extensions/ai.ts | 18 + packages/sdk/src/hooks/use-ai.ts | 63 ++++ packages/sdk/tailwind.config.js | 11 +- pnpm-lock.yaml | 355 +++++++++++++++++- 10 files changed, 556 insertions(+), 32 deletions(-) create mode 100644 packages/sdk/src/components/editor/formula/extensions/ai.ts create mode 100644 packages/sdk/src/hooks/use-ai.ts diff --git a/apps/nextjs-app/.env.example b/apps/nextjs-app/.env.example index 9c7671d55a..ba6526b7ff 100644 --- a/apps/nextjs-app/.env.example +++ b/apps/nextjs-app/.env.example @@ -164,3 +164,8 @@ NEXT_BUILD_ENV_SENTRY_DEBUG=false NEXT_BUILD_ENV_SENTRY_TRACING=false # enable nextjs image optimization NEXT_ENV_IMAGES_ALL_REMOTE=true + +# openai api key +OPENAI_API_KEY="xxxxxxxxx" +# openai base url +OPENAI_BASE_URL="xxxxxxxxx" diff --git a/apps/nextjs-app/src/styles/global.css b/apps/nextjs-app/src/styles/global.css index daf75a4939..2739301562 100644 --- a/apps/nextjs-app/src/styles/global.css +++ b/apps/nextjs-app/src/styles/global.css @@ -130,3 +130,11 @@ body { .fc-event:focus:after { @apply bg-transparent !important; } + +.cm-placeholder { + color: hsl(var(--muted)); + display: inline-block; + pointer-events: none; + padding-left: 0.3rem; + font-size: small; +} diff --git a/packages/common-i18n/src/locales/en/sdk.json b/packages/common-i18n/src/locales/en/sdk.json index 48881fc690..f975ba5bc4 100644 --- a/packages/common-i18n/src/locales/en/sdk.json +++ b/packages/common-i18n/src/locales/en/sdk.json @@ -54,7 +54,8 @@ "guideSyntax": "Syntax", "guideExample": "Example", "helperExample": "Example: ", - "fieldValue": "Returns the value to the cells of the {{fieldName}} field." + "fieldValue": "Returns the value to the cells of the {{fieldName}} field.", + "placeholder": "Enter an expression or press // to generate with AI" }, "link": { "placeholder": "Select records to link", diff --git a/packages/sdk/src/components/comment/comment-editor/plate-ui/icons.tsx b/packages/sdk/src/components/comment/comment-editor/plate-ui/icons.tsx index d4a262671c..66d2c12468 100644 --- a/packages/sdk/src/components/comment/comment-editor/plate-ui/icons.tsx +++ b/packages/sdk/src/components/comment/comment-editor/plate-ui/icons.tsx @@ -370,6 +370,38 @@ const LayoutIcon = (props: LucideProps) => ( ); +export const MagicAI = ( + props: LucideProps & { + active?: boolean; + } +) => ( + + + + + +); + export const Icons = { LayoutIcon, add: Plus, diff --git a/packages/sdk/src/components/editor/formula/Editor.tsx b/packages/sdk/src/components/editor/formula/Editor.tsx index fe598f9738..d130f7648b 100644 --- a/packages/sdk/src/components/editor/formula/Editor.tsx +++ b/packages/sdk/src/components/editor/formula/Editor.tsx @@ -11,27 +11,30 @@ import { CharStreams } from 'antlr4ts'; import Fuse from 'fuse.js'; import { cloneDeep, keyBy } from 'lodash'; import type { FC } from 'react'; -import { useRef, useState, useMemo, useCallback } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from '../../../context/app/i18n'; import { useFieldStaticGetter, useFields } from '../../../hooks'; import { FormulaField } from '../../../model'; import type { ICodeEditorRef } from './components'; -import { FunctionGuide, FunctionHelper, CodeEditor } from './components'; +import { CodeEditor, FunctionGuide, FunctionHelper } from './components'; import { - Type2IconMap, FOCUS_TOKENS_SET, - useFunctionsDisplayMap, + Type2IconMap, useFormulaFunctionsMap, + useFunctionsDisplayMap, } from './constants'; import { THEME_EXTENSIONS, TOKEN_EXTENSIONS, getVariableExtensions } from './extensions'; -import { SuggestionItemType } from './interface'; +import { getFormulaPrompt } from './extensions/ai'; import type { IFocusToken, IFuncHelpData, IFunctionCollectionItem, IFunctionSchema, } from './interface'; +import { SuggestionItemType } from './interface'; import { FormulaNodePathVisitor } from './visitor'; +import { useAIStream } from '../../../hooks/use-ai'; +import { MagicAI } from '../../comment/comment-editor/plate-ui/icons'; interface IFormulaEditorProps { expression?: string; @@ -63,6 +66,13 @@ export const FormulaEditor: FC = (props) => { [formulaFunctionsMap] ); const functionsDisplayMap = useFunctionsDisplayMap(); + const { generateAIResponse, text, loading } = useAIStream(); + + useEffect(() => { + if (text) { + setExpressionByName(text); + } + }, [text]); const filteredFields = useMemo(() => { const fuse = new Fuse(fields, { @@ -329,20 +339,44 @@ export const FormulaEditor: FC = (props) => { } }; + const handleGenerateFormula = useCallback(() => { + if (!expressionByName || loading) return; + if (expressionByName.startsWith('//')) { + generateAIResponse(getFormulaPrompt(expressionByName.slice(2), fields)); + } + }, [expressionByName, fields, loading]); const codeBg = isLightTheme ? 'bg-slate-100' : 'bg-gray-900'; + // only generate formula when the expression starts with // + const isReadyToGenerate = expressionByName.startsWith('//'); + return (
-

{t('editor.formula.title')}

+
+

{t('editor.formula.title')}

+ +
-
+ +
{errMsg}
diff --git a/packages/sdk/src/components/editor/formula/components/CodeEditor.tsx b/packages/sdk/src/components/editor/formula/components/CodeEditor.tsx index 1a01496b9c..7e7db8bd5f 100644 --- a/packages/sdk/src/components/editor/formula/components/CodeEditor.tsx +++ b/packages/sdk/src/components/editor/formula/components/CodeEditor.tsx @@ -1,7 +1,7 @@ import { syntaxHighlighting, defaultHighlightStyle } from '@codemirror/language'; import type { EditorSelection, Extension } from '@codemirror/state'; import { EditorState, StateEffect } from '@codemirror/state'; -import { EditorView } from '@codemirror/view'; +import { EditorView, Decoration, WidgetType } from '@codemirror/view'; import type { ForwardRefRenderFunction } from 'react'; import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef } from 'react'; import { AUTOCOMPLETE_EXTENSIONS, HISTORY_EXTENSIONS } from '../extensions'; @@ -11,6 +11,7 @@ interface ICodeEditorProps { extensions?: Extension[]; onChange?: (value: string) => void; onSelectionChange?: (value: string, selection: EditorSelection) => void; + placeholder?: string; } export interface ICodeEditorRef { @@ -20,7 +21,13 @@ export interface ICodeEditorRef { const emptyExtensions: Extension[] = []; const CodeEditorBase: ForwardRefRenderFunction = (props, ref) => { - const { value = '', extensions = emptyExtensions, onChange, onSelectionChange } = props; + const { + value = '', + extensions = emptyExtensions, + onChange, + onSelectionChange, + placeholder, + } = props; const editorRef = useRef(null); const editorViewRef = useRef(null); @@ -42,15 +49,38 @@ const CodeEditorBase: ForwardRefRenderFunction } }); const highlight = syntaxHighlighting(defaultHighlightStyle, { fallback: true }); + + const placeholderExt = placeholder + ? EditorView.decorations.of((view) => { + const doc = view.state.doc; + return doc.length === 0 + ? Decoration.set([ + Decoration.widget({ + widget: new (class extends WidgetType { + toDOM() { + const span = document.createElement('span'); + span.className = 'cm-placeholder'; + span.textContent = placeholder; + return span; + } + })(), + side: 1, + }).range(0), + ]) + : Decoration.none; + }) + : []; + return [ ...HISTORY_EXTENSIONS, ...AUTOCOMPLETE_EXTENSIONS, highlight, updateListener, EditorView.lineWrapping, + placeholderExt, ...extensions, ]; - }, [extensions, onChange, onSelectionChange]); + }, [extensions, onChange, onSelectionChange, placeholder]); useEffect(() => { if (!editorRef.current) return; @@ -73,6 +103,15 @@ const CodeEditorBase: ForwardRefRenderFunction // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + useEffect(() => { + if (editorViewRef.current) { + const transaction = editorViewRef.current.state.update({ + changes: { from: 0, to: editorViewRef.current.state.doc.length, insert: value }, + }); + editorViewRef.current.dispatch(transaction); + } + }, [value]); + useEffect(() => { editorViewRef.current?.dispatch({ effects: StateEffect.reconfigure.of(allExtensions) }); }, [allExtensions]); diff --git a/packages/sdk/src/components/editor/formula/extensions/ai.ts b/packages/sdk/src/components/editor/formula/extensions/ai.ts new file mode 100644 index 0000000000..6744e0448f --- /dev/null +++ b/packages/sdk/src/components/editor/formula/extensions/ai.ts @@ -0,0 +1,18 @@ +import { useFields } from '../../../../hooks/use-fields'; + +export const getFormulaPrompt = (prompt: string, fields: ReturnType) => { + const context = fields.map((field) => `${field.id}: ${field.name}`).join('\n'); + return ` + you are a expert of airtable formula, especially good at writing formula. + 1. please generate a airtable formula based on the user's description. + 2. only return the formula, no need to explain. do not use \`\` to wrap it. when referencing by field name, use \`{}\` to wrap it. + 3. the field information of the current table is in the tag, please refer to the field information to generate the formula. + 4. the user's description is in the tag. + + ${context} + + + ${prompt} + + `; +}; diff --git a/packages/sdk/src/hooks/use-ai.ts b/packages/sdk/src/hooks/use-ai.ts new file mode 100644 index 0000000000..7734f52ebe --- /dev/null +++ b/packages/sdk/src/hooks/use-ai.ts @@ -0,0 +1,63 @@ +import { useCallback, useState, useRef } from 'react'; + +const API_ENDPOINT = '/api/ai'; + +interface IUseAIStreamOptions { + timeout?: number; // unit: ms +} + +export const useAIStream = (options?: IUseAIStreamOptions) => { + const { timeout = 30000 } = options || {}; + const [loading, setLoading] = useState(false); + const [text, setText] = useState(''); + const [error, setError] = useState(null); + const controllerRef = useRef(null); + + const generateAIResponse = useCallback(async (prompt: string) => { + setText(''); + setError(null); + setLoading(true); + + controllerRef.current = new AbortController(); + const timeoutId = setTimeout(() => controllerRef.current?.abort(), timeout); + + try { + const result = await fetch(API_ENDPOINT, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ prompt }), + signal: controllerRef.current.signal, + }); + + if (!result.ok) { + throw new Error(`HTTP error! status: ${result.status}`); + } + + const reader = result.body?.getReader(); + if (!reader) throw new Error('No reader available'); + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + const chunk = new TextDecoder().decode(value); + setText((prev) => prev + chunk); + } + } catch (error) { + const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + setError(errorMessage); + console.error('Error streaming AI response:', error); + } finally { + clearTimeout(timeoutId); + setLoading(false); + } + }, []); + + const stop = useCallback(() => { + controllerRef.current?.abort(); + }, []); + + return { text, generateAIResponse, loading, error, stop }; +}; diff --git a/packages/sdk/tailwind.config.js b/packages/sdk/tailwind.config.js index 38cb123190..47e78bcdbe 100644 --- a/packages/sdk/tailwind.config.js +++ b/packages/sdk/tailwind.config.js @@ -7,6 +7,15 @@ const buildFilePath = join(__dirname, './dist/**/*.{js,ts,jsx,tsx}'); module.exports = uiConfig({ content: [sdkPath, buildFilePath], darkMode: ['class'], - theme: {}, + theme: { + extend: { + keyframes: { + scale: { + '0%, 100%': { transform: 'scale(1)' }, + '50%': { transform: 'scale(0.8)' }, + }, + }, + }, + }, plugins: [], }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2dcecb610b..b3797d3b63 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -57,6 +57,9 @@ importers: apps/nestjs-backend: dependencies: + '@ai-sdk/openai': + specifier: 0.0.72 + version: 0.0.72(zod@3.22.4) '@aws-sdk/client-s3': specifier: 3.609.0 version: 3.609.0 @@ -162,6 +165,9 @@ importers: '@types/papaparse': specifier: 5.3.14 version: 5.3.14 + ai: + specifier: 3.4.33 + version: 3.4.33(react@18.3.1)(sswr@2.1.0(svelte@5.2.10))(svelte@5.2.10)(vue@3.5.12(typescript@5.4.3))(zod@3.22.4) ajv: specifier: 8.12.0 version: 8.12.0 @@ -2048,6 +2054,73 @@ packages: '@adobe/css-tools@4.3.3': resolution: {integrity: sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==} + '@ai-sdk/openai@0.0.72': + resolution: {integrity: sha512-IKsgxIt6KJGkEHyMp975xW5VPmetwhI8g9H6dDmwvemBB41IRQa78YMNttiJqPcgmrZX2QfErOICv1gQvZ1gZg==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/provider-utils@1.0.22': + resolution: {integrity: sha512-YHK2rpj++wnLVc9vPGzGFP3Pjeld2MwhKinetA0zKXOoHAT/Jit5O8kZsxcSlJPu9wvcGT1UGZEjZrtO7PfFOQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + peerDependenciesMeta: + zod: + optional: true + + '@ai-sdk/provider@0.0.26': + resolution: {integrity: sha512-dQkfBDs2lTYpKM8389oopPdQgIU007GQyCbuPPrV+K6MtSII3HBfE0stUIMXUb44L+LK1t6GXPP7wjSzjO6uKg==} + engines: {node: '>=18'} + + '@ai-sdk/react@0.0.70': + resolution: {integrity: sha512-GnwbtjW4/4z7MleLiW+TOZC2M29eCg1tOUpuEiYFMmFNZK8mkrqM0PFZMo6UsYeUYMWqEOOcPOU9OQVJMJh7IQ==} + engines: {node: '>=18'} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + zod: ^3.0.0 + peerDependenciesMeta: + react: + optional: true + zod: + optional: true + + '@ai-sdk/solid@0.0.54': + resolution: {integrity: sha512-96KWTVK+opdFeRubqrgaJXoNiDP89gNxFRWUp0PJOotZW816AbhUf4EnDjBjXTLjXL1n0h8tGSE9sZsRkj9wQQ==} + engines: {node: '>=18'} + peerDependencies: + solid-js: ^1.7.7 + peerDependenciesMeta: + solid-js: + optional: true + + '@ai-sdk/svelte@0.0.57': + resolution: {integrity: sha512-SyF9ItIR9ALP9yDNAD+2/5Vl1IT6kchgyDH8xkmhysfJI6WrvJbtO1wdQ0nylvPLcsPoYu+cAlz1krU4lFHcYw==} + engines: {node: '>=18'} + peerDependencies: + svelte: ^3.0.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true + + '@ai-sdk/ui-utils@0.0.50': + resolution: {integrity: sha512-Z5QYJVW+5XpSaJ4jYCCAVG7zIAuKOOdikhgpksneNmKvx61ACFaf98pmOd+xnjahl0pIlc/QIe6O4yVaJ1sEaw==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + peerDependenciesMeta: + zod: + optional: true + + '@ai-sdk/vue@0.0.59': + resolution: {integrity: sha512-+ofYlnqdc8c4F6tM0IKF0+7NagZRAiqBJpGDJ+6EYhDW8FHLUP/JFBgu32SjxSxC6IKFZxEnl68ZoP/Z38EMlw==} + engines: {node: '>=18'} + peerDependencies: + vue: ^3.3.4 + peerDependenciesMeta: + vue: + optional: true + '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} @@ -7034,6 +7107,9 @@ packages: '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/diff-match-patch@1.0.36': + resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==} + '@types/doctrine@0.0.3': resolution: {integrity: sha512-w5jZ0ee+HaPOaX25X2/2oGR/7rgAQSYII7X7pp0m9KgBfMP7uKfMfTvcpl5Dj+eDBbpxKGiqE+flqDr6XTd2RA==} @@ -8293,6 +8369,11 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn-typescript@1.4.13: + resolution: {integrity: sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==} + peerDependencies: + acorn: '>=8.9.0' + acorn-walk@7.2.0: resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} engines: {node: '>=0.4.0'} @@ -8350,6 +8431,27 @@ packages: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} + ai@3.4.33: + resolution: {integrity: sha512-plBlrVZKwPoRTmM8+D1sJac9Bq8eaa2jiZlHLZIWekKWI1yMWYZvCCEezY9ASPwRhULYDJB2VhKOBUUeg3S5JQ==} + engines: {node: '>=18'} + peerDependencies: + openai: ^4.42.0 + react: ^18 || ^19 || ^19.0.0-rc + sswr: ^2.1.0 + svelte: ^3.0.0 || ^4.0.0 || ^5.0.0 + zod: ^3.0.0 + peerDependenciesMeta: + openai: + optional: true + react: + optional: true + sswr: + optional: true + svelte: + optional: true + zod: + optional: true + airbnb-js-shims@2.2.1: resolution: {integrity: sha512-wJNXPH66U2xjgo1Zwyjf9EydvJ2Si94+vSdk6EERcBfB2VZkeltpqIats0cqIZMLCXP3zcyaUKGYQeIBT6XjsQ==} @@ -8515,6 +8617,10 @@ packages: aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + arr-diff@4.0.0: resolution: {integrity: sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==} engines: {node: '>=0.10.0'} @@ -9905,6 +10011,9 @@ packages: didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + diff-match-patch@1.0.5: + resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} + diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -10532,6 +10641,9 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true + esm-env@1.2.0: + resolution: {integrity: sha512-OhSQuHL3mUcaQHjGe8UMG8GsJIJHYYz0flR0h9fiTPNMupLMkb7TvcRD0EeJXW5a8GHBgfz08b6FDLNK7kkPQA==} + esm@3.2.25: resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} engines: {node: '>=6'} @@ -10554,6 +10666,9 @@ packages: resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} + esrap@1.2.2: + resolution: {integrity: sha512-F2pSJklxx1BlQIQgooczXCPHmcWpn6EsP5oo73LQfonG9fIlIENQ8vMmfGXeojP9MrkzUNAfyU5vdFlR9shHAw==} + esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} @@ -11897,6 +12012,9 @@ packages: is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} @@ -12190,6 +12308,9 @@ packages: json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -12211,6 +12332,11 @@ packages: jsonc-parser@3.2.1: resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==} + jsondiffpatch@0.6.0: + resolution: {integrity: sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} @@ -12418,6 +12544,9 @@ packages: localforage@1.10.0: resolution: {integrity: sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==} + locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + locate-path@3.0.0: resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} engines: {node: '>=6'} @@ -15883,6 +16012,11 @@ packages: resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==} engines: {node: '>= 8'} + sswr@2.1.0: + resolution: {integrity: sha512-Cqc355SYlTAaUt8iDPaC/4DPPXK925PePLMxyBKuWd5kKc5mwsG3nT9+Mq2tyguL5s7b4Jg+IRMpTRsNTAfpSQ==} + peerDependencies: + svelte: ^4.0.0 || ^5.0.0-next.0 + stable@0.1.8: resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' @@ -16156,6 +16290,10 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + svelte@5.2.10: + resolution: {integrity: sha512-ON0OyO7vOmSjTc9mLjusu3vf1I7BvjovbiRB7j84F1WZMXV6dR+Tj4btIzxQxMHfzbGskaFmRa7qjgmBSVBnhQ==} + engines: {node: '>=18'} + svg-parser@2.0.4: resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} @@ -16172,6 +16310,19 @@ packages: swagger-ui-dist@5.11.2: resolution: {integrity: sha512-jQG0cRgJNMZ7aCoiFofnoojeSaa/+KgWaDlfgs8QN+BXoGMpxeMVY5OEnjq4OlNvF3yjftO8c9GRAgcHlO+u7A==} + swr@2.2.5: + resolution: {integrity: sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==} + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 + + swrev@4.0.0: + resolution: {integrity: sha512-LqVcOHSB4cPGgitD1riJ1Hh4vdmITOp+BkmfmXRh4hSF/t7EnS4iD+SOTmq7w5pPm/SiPeto4ADbKS6dHUDWFA==} + + swrv@1.0.4: + resolution: {integrity: sha512-zjEkcP8Ywmj+xOJW3lIT65ciY/4AL4e/Or7Gj0MzU3zBJNMdJiT8geVZhINavnlHRMMCcJLHhraLTAiDOTmQ9g==} + peerDependencies: + vue: '>=3.2.26 < 4' + symbol-observable@4.0.0: resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==} engines: {node: '>=0.10'} @@ -16323,6 +16474,10 @@ packages: resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==} engines: {node: '>=10'} + throttleit@2.1.0: + resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==} + engines: {node: '>=18'} + through2@2.0.5: resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} @@ -17404,12 +17559,20 @@ packages: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} + zimmerframe@1.1.2: + resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} + zod-i18n-map@2.27.0: resolution: {integrity: sha512-ORu9XpiVh3WDiEUs5Cr9siGgnpeODoBsTIgSD8sQCH9B//f9KowlzqHUEdPYb3vFonaSH8yPvPCOFM4niwp3Sg==} peerDependencies: i18next: '>=21.3.0' zod: '>=3.17.0' + zod-to-json-schema@3.23.5: + resolution: {integrity: sha512-5wlSS0bXfF/BrL4jPAbz9da5hDlDptdEppYfe+x4eIJ7jioqKG9uUxOwPzqof09u/XeVdrgFu29lZi+8XNDJtA==} + peerDependencies: + zod: ^3.23.3 + zod-validation-error@3.0.3: resolution: {integrity: sha512-cETTrcMq3Ze58vhdR0zD37uJm/694I6mAxcf/ei5bl89cC++fBNxrC2z8lkFze/8hVMPwrbtrwXHR2LB50fpHw==} engines: {node: '>=18.0.0'} @@ -17456,6 +17619,72 @@ snapshots: '@adobe/css-tools@4.3.3': {} + '@ai-sdk/openai@0.0.72(zod@3.22.4)': + dependencies: + '@ai-sdk/provider': 0.0.26 + '@ai-sdk/provider-utils': 1.0.22(zod@3.22.4) + zod: 3.22.4 + + '@ai-sdk/provider-utils@1.0.22(zod@3.22.4)': + dependencies: + '@ai-sdk/provider': 0.0.26 + eventsource-parser: 1.1.2 + nanoid: 3.3.7 + secure-json-parse: 2.7.0 + optionalDependencies: + zod: 3.22.4 + + '@ai-sdk/provider@0.0.26': + dependencies: + json-schema: 0.4.0 + + '@ai-sdk/react@0.0.70(react@18.3.1)(zod@3.22.4)': + dependencies: + '@ai-sdk/provider-utils': 1.0.22(zod@3.22.4) + '@ai-sdk/ui-utils': 0.0.50(zod@3.22.4) + swr: 2.2.5(react@18.3.1) + throttleit: 2.1.0 + optionalDependencies: + react: 18.3.1 + zod: 3.22.4 + + '@ai-sdk/solid@0.0.54(zod@3.22.4)': + dependencies: + '@ai-sdk/provider-utils': 1.0.22(zod@3.22.4) + '@ai-sdk/ui-utils': 0.0.50(zod@3.22.4) + transitivePeerDependencies: + - zod + + '@ai-sdk/svelte@0.0.57(svelte@5.2.10)(zod@3.22.4)': + dependencies: + '@ai-sdk/provider-utils': 1.0.22(zod@3.22.4) + '@ai-sdk/ui-utils': 0.0.50(zod@3.22.4) + sswr: 2.1.0(svelte@5.2.10) + optionalDependencies: + svelte: 5.2.10 + transitivePeerDependencies: + - zod + + '@ai-sdk/ui-utils@0.0.50(zod@3.22.4)': + dependencies: + '@ai-sdk/provider': 0.0.26 + '@ai-sdk/provider-utils': 1.0.22(zod@3.22.4) + json-schema: 0.4.0 + secure-json-parse: 2.7.0 + zod-to-json-schema: 3.23.5(zod@3.22.4) + optionalDependencies: + zod: 3.22.4 + + '@ai-sdk/vue@0.0.59(vue@3.5.12(typescript@5.4.3))(zod@3.22.4)': + dependencies: + '@ai-sdk/provider-utils': 1.0.22(zod@3.22.4) + '@ai-sdk/ui-utils': 0.0.50(zod@3.22.4) + swrv: 1.0.4(vue@3.5.12(typescript@5.4.3)) + optionalDependencies: + vue: 3.5.12(typescript@5.4.3) + transitivePeerDependencies: + - zod + '@alloc/quick-lru@5.2.0': {} '@ampproject/remapping@2.3.0': @@ -23568,6 +23797,8 @@ snapshots: dependencies: '@types/ms': 0.7.34 + '@types/diff-match-patch@1.0.36': {} + '@types/doctrine@0.0.3': {} '@types/ejs@3.1.5': @@ -24952,13 +25183,11 @@ snapshots: entities: 4.5.0 estree-walker: 2.0.2 source-map-js: 1.2.1 - optional: true '@vue/compiler-dom@3.5.12': dependencies: '@vue/compiler-core': 3.5.12 '@vue/shared': 3.5.12 - optional: true '@vue/compiler-sfc@3.5.12': dependencies: @@ -24971,24 +25200,20 @@ snapshots: magic-string: 0.30.12 postcss: 8.4.47 source-map-js: 1.2.1 - optional: true '@vue/compiler-ssr@3.5.12': dependencies: '@vue/compiler-dom': 3.5.12 '@vue/shared': 3.5.12 - optional: true '@vue/reactivity@3.5.12': dependencies: '@vue/shared': 3.5.12 - optional: true '@vue/runtime-core@3.5.12': dependencies: '@vue/reactivity': 3.5.12 '@vue/shared': 3.5.12 - optional: true '@vue/runtime-dom@3.5.12': dependencies: @@ -24996,17 +25221,14 @@ snapshots: '@vue/runtime-core': 3.5.12 '@vue/shared': 3.5.12 csstype: 3.1.3 - optional: true '@vue/server-renderer@3.5.12(vue@3.5.12(typescript@5.4.3))': dependencies: '@vue/compiler-ssr': 3.5.12 '@vue/shared': 3.5.12 vue: 3.5.12(typescript@5.4.3) - optional: true - '@vue/shared@3.5.12': - optional: true + '@vue/shared@3.5.12': {} '@webassemblyjs/ast@1.12.1': dependencies: @@ -25234,6 +25456,10 @@ snapshots: dependencies: acorn: 8.11.3 + acorn-typescript@1.4.13(acorn@8.12.1): + dependencies: + acorn: 8.12.1 + acorn-walk@7.2.0: {} acorn-walk@8.3.2: {} @@ -25276,6 +25502,30 @@ snapshots: clean-stack: 2.2.0 indent-string: 4.0.0 + ai@3.4.33(react@18.3.1)(sswr@2.1.0(svelte@5.2.10))(svelte@5.2.10)(vue@3.5.12(typescript@5.4.3))(zod@3.22.4): + dependencies: + '@ai-sdk/provider': 0.0.26 + '@ai-sdk/provider-utils': 1.0.22(zod@3.22.4) + '@ai-sdk/react': 0.0.70(react@18.3.1)(zod@3.22.4) + '@ai-sdk/solid': 0.0.54(zod@3.22.4) + '@ai-sdk/svelte': 0.0.57(svelte@5.2.10)(zod@3.22.4) + '@ai-sdk/ui-utils': 0.0.50(zod@3.22.4) + '@ai-sdk/vue': 0.0.59(vue@3.5.12(typescript@5.4.3))(zod@3.22.4) + '@opentelemetry/api': 1.9.0 + eventsource-parser: 1.1.2 + json-schema: 0.4.0 + jsondiffpatch: 0.6.0 + secure-json-parse: 2.7.0 + zod-to-json-schema: 3.23.5(zod@3.22.4) + optionalDependencies: + react: 18.3.1 + sswr: 2.1.0(svelte@5.2.10) + svelte: 5.2.10 + zod: 3.22.4 + transitivePeerDependencies: + - solid-js + - vue + airbnb-js-shims@2.2.1: dependencies: array-includes: 3.1.8 @@ -25447,6 +25697,8 @@ snapshots: dependencies: dequal: 2.0.3 + aria-query@5.3.2: {} + arr-diff@4.0.0: {} arr-flatten@1.1.0: {} @@ -27075,6 +27327,8 @@ snapshots: didyoumean@1.2.2: {} + diff-match-patch@1.0.5: {} + diff@4.0.2: {} diff@5.2.0: {} @@ -27670,8 +27924,8 @@ snapshots: '@typescript-eslint/parser': 8.9.0(eslint@8.57.0)(typescript@5.4.3) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.10.0(eslint@8.57.0) eslint-plugin-react: 7.37.1(eslint@8.57.0) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.0) @@ -27711,19 +27965,19 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0): + eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.3.7 enhanced-resolve: 5.17.1 eslint: 8.57.0 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.8.1 is-bun-module: 1.2.1 is-glob: 4.0.3 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) transitivePeerDependencies: - '@typescript-eslint/parser' - eslint-import-resolver-node @@ -27750,14 +28004,14 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.9.0(eslint@8.57.0)(typescript@5.4.3) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0) transitivePeerDependencies: - supports-color @@ -27799,7 +28053,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -27810,7 +28064,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.9.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -28072,6 +28326,8 @@ snapshots: transitivePeerDependencies: - supports-color + esm-env@1.2.0: {} + esm@3.2.25: {} espree@9.6.1: @@ -28088,6 +28344,11 @@ snapshots: dependencies: estraverse: 5.3.0 + esrap@1.2.2: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + '@types/estree': 1.0.6 + esrecurse@4.3.0: dependencies: estraverse: 5.3.0 @@ -29701,6 +29962,10 @@ snapshots: dependencies: '@types/estree': 1.0.5 + is-reference@3.0.3: + dependencies: + '@types/estree': 1.0.6 + is-regex@1.1.4: dependencies: call-bind: 1.0.7 @@ -29982,6 +30247,8 @@ snapshots: json-schema-traverse@1.0.0: {} + json-schema@0.4.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} json-stream@1.0.0: {} @@ -29996,6 +30263,12 @@ snapshots: jsonc-parser@3.2.1: {} + jsondiffpatch@0.6.0: + dependencies: + '@types/diff-match-patch': 1.0.36 + chalk: 5.3.0 + diff-match-patch: 1.0.5 + jsonfile@4.0.0: optionalDependencies: graceful-fs: 4.2.11 @@ -30230,6 +30503,8 @@ snapshots: dependencies: lie: 3.1.1 + locate-character@3.0.0: {} + locate-path@3.0.0: dependencies: p-locate: 3.0.0 @@ -34849,6 +35124,11 @@ snapshots: minipass: 3.3.6 optional: true + sswr@2.1.0(svelte@5.2.10): + dependencies: + svelte: 5.2.10 + swrev: 4.0.0 + stable@0.1.8: {} stack-generator@2.0.10: @@ -35138,6 +35418,22 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + svelte@5.2.10: + dependencies: + '@ampproject/remapping': 2.3.0 + '@jridgewell/sourcemap-codec': 1.5.0 + '@types/estree': 1.0.6 + acorn: 8.12.1 + acorn-typescript: 1.4.13(acorn@8.12.1) + aria-query: 5.3.2 + axobject-query: 4.1.0 + esm-env: 1.2.0 + esrap: 1.2.2 + is-reference: 3.0.3 + locate-character: 3.0.0 + magic-string: 0.30.12 + zimmerframe: 1.1.2 + svg-parser@2.0.4: {} svgo@2.8.0: @@ -35162,6 +35458,18 @@ snapshots: swagger-ui-dist@5.11.2: {} + swr@2.2.5(react@18.3.1): + dependencies: + client-only: 0.0.1 + react: 18.3.1 + use-sync-external-store: 1.2.0(react@18.3.1) + + swrev@4.0.0: {} + + swrv@1.0.4(vue@3.5.12(typescript@5.4.3)): + dependencies: + vue: 3.5.12(typescript@5.4.3) + symbol-observable@4.0.0: {} symbol.prototype.description@1.0.6: @@ -35399,6 +35707,8 @@ snapshots: throttle-debounce@3.0.1: {} + throttleit@2.1.0: {} + through2@2.0.5: dependencies: readable-stream: 2.3.8 @@ -36167,7 +36477,6 @@ snapshots: '@vue/shared': 3.5.12 optionalDependencies: typescript: 5.4.3 - optional: true w3c-keyname@2.2.8: {} @@ -36662,11 +36971,17 @@ snapshots: yocto-queue@1.0.0: {} + zimmerframe@1.1.2: {} + zod-i18n-map@2.27.0(i18next@23.10.1)(zod@3.22.4): dependencies: i18next: 23.10.1 zod: 3.22.4 + zod-to-json-schema@3.23.5(zod@3.22.4): + dependencies: + zod: 3.22.4 + zod-validation-error@3.0.3(zod@3.22.4): dependencies: zod: 3.22.4 From 30f293070b7b02b79d7d6017388033ee6904d7dc Mon Sep 17 00:00:00 2001 From: mayneyao Date: Fri, 8 Nov 2024 15:11:12 +0800 Subject: [PATCH 03/20] chore: fix lint --- apps/nestjs-backend/src/app.module.ts | 2 +- apps/nestjs-backend/src/features/ai/ai.module.ts | 2 +- apps/nestjs-backend/src/features/ai/ai.service.ts | 5 ++--- .../sdk/src/components/editor/formula/Editor.tsx | 6 +++--- .../src/components/editor/formula/extensions/ai.ts | 2 +- packages/sdk/src/hooks/use-ai.ts | 13 +++++++++---- 6 files changed, 17 insertions(+), 13 deletions(-) diff --git a/apps/nestjs-backend/src/app.module.ts b/apps/nestjs-backend/src/app.module.ts index 5c7402241f..1e4c9cbe1b 100644 --- a/apps/nestjs-backend/src/app.module.ts +++ b/apps/nestjs-backend/src/app.module.ts @@ -7,6 +7,7 @@ import type { ICacheConfig } from './configs/cache.config'; import { ConfigModule } from './configs/config.module'; import { AccessTokenModule } from './features/access-token/access-token.module'; import { AggregationOpenApiModule } from './features/aggregation/open-api/aggregation-open-api.module'; +import { AiModule } from './features/ai/ai.module'; import { AttachmentsModule } from './features/attachments/attachments.module'; import { AuthModule } from './features/auth/auth.module'; import { BaseModule } from './features/base/base.module'; @@ -35,7 +36,6 @@ import { GlobalModule } from './global/global.module'; import { InitBootstrapProvider } from './global/init-bootstrap.provider'; import { LoggerModule } from './logger/logger.module'; import { WsModule } from './ws/ws.module'; -import { AiModule } from './features/ai/ai.module'; export const appModules = { imports: [ diff --git a/apps/nestjs-backend/src/features/ai/ai.module.ts b/apps/nestjs-backend/src/features/ai/ai.module.ts index ab10fa97a9..746462ee1d 100644 --- a/apps/nestjs-backend/src/features/ai/ai.module.ts +++ b/apps/nestjs-backend/src/features/ai/ai.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { AiController } from './ai.controller'; import { ConfigModule } from '@nestjs/config'; +import { AiController } from './ai.controller'; import { AiService } from './ai.service'; @Module({ diff --git a/apps/nestjs-backend/src/features/ai/ai.service.ts b/apps/nestjs-backend/src/features/ai/ai.service.ts index ce936ca65e..4d4342e88b 100644 --- a/apps/nestjs-backend/src/features/ai/ai.service.ts +++ b/apps/nestjs-backend/src/features/ai/ai.service.ts @@ -1,6 +1,6 @@ +import { createOpenAI } from '@ai-sdk/openai'; import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import { createOpenAI } from '@ai-sdk/openai'; import { streamText } from 'ai'; @Injectable() @@ -14,10 +14,9 @@ export class AiService { baseURL: openAIBaseUrl, apiKey: openaiApiKey, }); - const result = await streamText({ + return await streamText({ model: openai('gpt-4o-mini'), prompt: prompt, }); - return result; } } diff --git a/packages/sdk/src/components/editor/formula/Editor.tsx b/packages/sdk/src/components/editor/formula/Editor.tsx index d130f7648b..0595da6bbd 100644 --- a/packages/sdk/src/components/editor/formula/Editor.tsx +++ b/packages/sdk/src/components/editor/formula/Editor.tsx @@ -14,7 +14,9 @@ import type { FC } from 'react'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from '../../../context/app/i18n'; import { useFieldStaticGetter, useFields } from '../../../hooks'; +import { useAIStream } from '../../../hooks/use-ai'; import { FormulaField } from '../../../model'; +import { MagicAI } from '../../comment/comment-editor/plate-ui/icons'; import type { ICodeEditorRef } from './components'; import { CodeEditor, FunctionGuide, FunctionHelper } from './components'; import { @@ -33,8 +35,6 @@ import type { } from './interface'; import { SuggestionItemType } from './interface'; import { FormulaNodePathVisitor } from './visitor'; -import { useAIStream } from '../../../hooks/use-ai'; -import { MagicAI } from '../../comment/comment-editor/plate-ui/icons'; interface IFormulaEditorProps { expression?: string; @@ -369,7 +369,7 @@ export const FormulaEditor: FC = (props) => {
-
+
) => { const context = fields.map((field) => `${field.id}: ${field.name}`).join('\n'); diff --git a/packages/sdk/src/hooks/use-ai.ts b/packages/sdk/src/hooks/use-ai.ts index 7734f52ebe..8b600811a5 100644 --- a/packages/sdk/src/hooks/use-ai.ts +++ b/packages/sdk/src/hooks/use-ai.ts @@ -1,6 +1,6 @@ import { useCallback, useState, useRef } from 'react'; -const API_ENDPOINT = '/api/ai'; +const aiApiEndpoint = '/api/ai'; interface IUseAIStreamOptions { timeout?: number; // unit: ms @@ -22,9 +22,10 @@ export const useAIStream = (options?: IUseAIStreamOptions) => { const timeoutId = setTimeout(() => controllerRef.current?.abort(), timeout); try { - const result = await fetch(API_ENDPOINT, { + const result = await fetch(aiApiEndpoint, { method: 'POST', headers: { + // eslint-disable-next-line @typescript-eslint/naming-convention 'Content-Type': 'application/json', }, body: JSON.stringify({ prompt }), @@ -38,9 +39,13 @@ export const useAIStream = (options?: IUseAIStreamOptions) => { const reader = result.body?.getReader(); if (!reader) throw new Error('No reader available'); - while (true) { + let reading = true; + while (reading) { const { done, value } = await reader.read(); - if (done) break; + if (done) { + reading = false; + break; + } const chunk = new TextDecoder().decode(value); setText((prev) => prev + chunk); From e2df8dff6f67c47cd3e4999734da123411303db5 Mon Sep 17 00:00:00 2001 From: mayneyao Date: Mon, 11 Nov 2024 15:59:51 +0800 Subject: [PATCH 04/20] feat(ai): store ai config in database --- .../features/setting/setting.controller.ts | 37 +++++++++++++++++-- .../src/features/setting/setting.service.ts | 10 ++++- .../prisma/postgres/schema.prisma | 1 + .../prisma/sqlite/schema.prisma | 1 + .../db-main-prisma/prisma/template.prisma | 1 + packages/openapi/src/admin/setting/get.ts | 2 + packages/openapi/src/admin/setting/update.ts | 20 ++++++++++ 7 files changed, 67 insertions(+), 5 deletions(-) diff --git a/apps/nestjs-backend/src/features/setting/setting.controller.ts b/apps/nestjs-backend/src/features/setting/setting.controller.ts index fa9d49351e..84db30c49b 100644 --- a/apps/nestjs-backend/src/features/setting/setting.controller.ts +++ b/apps/nestjs-backend/src/features/setting/setting.controller.ts @@ -1,16 +1,19 @@ import { Body, Controller, Get, Patch } from '@nestjs/common'; -import { IUpdateSettingRo, updateSettingRoSchema } from '@teable/openapi'; import type { ISettingVo } from '@teable/openapi'; +import { IUpdateSettingRo, updateSettingRoSchema } from '@teable/openapi'; import { ZodValidationPipe } from '../../zod.validation.pipe'; import { Permissions } from '../auth/decorators/permissions.decorator'; -import { Public } from '../auth/decorators/public.decorator'; import { SettingService } from './setting.service'; +import { Public } from '../auth/decorators/public.decorator'; @Controller('api/admin/setting') export class SettingController { constructor(private readonly settingService: SettingService) {} - @Public() + /** + * Get the instance settings, now we have config for AI, there are some sensitive fields, we need check the permission before return. + */ + @Permissions('instance|read') @Get() async getSetting(): Promise { return await this.settingService.getSetting(); @@ -22,6 +25,32 @@ export class SettingController { @Body(new ZodValidationPipe(updateSettingRoSchema)) updateSettingRo: IUpdateSettingRo ): Promise { - return await this.settingService.updateSetting(updateSettingRo); + const res = await this.settingService.updateSetting(updateSettingRo); + return { + ...res, + aiConfig: res.aiConfig ? JSON.parse(res.aiConfig) : null, + }; + } + + /** + * Public endpoint for getting public settings without authentication + */ + @Public() + @Get('public') + async getPublicSetting(): Promise< + Pick & { + aiConfig: { + enable: boolean; + }; + } + > { + const setting = await this.settingService.getSetting(); + const { aiConfig, ...rest } = setting; + return { + ...rest, + aiConfig: { + enable: aiConfig?.enable ?? false, + }, + }; } } diff --git a/apps/nestjs-backend/src/features/setting/setting.service.ts b/apps/nestjs-backend/src/features/setting/setting.service.ts index e439070a4e..da0b146348 100644 --- a/apps/nestjs-backend/src/features/setting/setting.service.ts +++ b/apps/nestjs-backend/src/features/setting/setting.service.ts @@ -14,8 +14,13 @@ export class SettingService { disallowSignUp: true, disallowSpaceCreation: true, disallowSpaceInvitation: true, + aiConfig: true, }, }) + .then((setting) => ({ + ...setting, + aiConfig: setting.aiConfig ? JSON.parse(setting.aiConfig as string) : null, + })) .catch(() => { throw new NotFoundException('Setting not found'); }); @@ -25,7 +30,10 @@ export class SettingService { const setting = await this.getSetting(); return await this.prismaService.setting.update({ where: { instanceId: setting.instanceId }, - data: updateSettingRo, + data: { + ...updateSettingRo, + aiConfig: updateSettingRo.aiConfig ? JSON.stringify(updateSettingRo.aiConfig) : null, + }, }); } } diff --git a/packages/db-main-prisma/prisma/postgres/schema.prisma b/packages/db-main-prisma/prisma/postgres/schema.prisma index 6ad19098e6..9817201c88 100644 --- a/packages/db-main-prisma/prisma/postgres/schema.prisma +++ b/packages/db-main-prisma/prisma/postgres/schema.prisma @@ -332,6 +332,7 @@ model Setting { disallowSignUp Boolean? @map("disallow_sign_up") disallowSpaceCreation Boolean? @map("disallow_space_creation") disallowSpaceInvitation Boolean? @map("disallow_space_invitation") + aiConfig String? @map("ai_config") @@map("setting") } diff --git a/packages/db-main-prisma/prisma/sqlite/schema.prisma b/packages/db-main-prisma/prisma/sqlite/schema.prisma index c307735416..cce178e5b4 100644 --- a/packages/db-main-prisma/prisma/sqlite/schema.prisma +++ b/packages/db-main-prisma/prisma/sqlite/schema.prisma @@ -332,6 +332,7 @@ model Setting { disallowSignUp Boolean? @map("disallow_sign_up") disallowSpaceCreation Boolean? @map("disallow_space_creation") disallowSpaceInvitation Boolean? @map("disallow_space_invitation") + aiConfig String? @map("ai_config") @@map("setting") } diff --git a/packages/db-main-prisma/prisma/template.prisma b/packages/db-main-prisma/prisma/template.prisma index 935af787eb..47bd445bbb 100644 --- a/packages/db-main-prisma/prisma/template.prisma +++ b/packages/db-main-prisma/prisma/template.prisma @@ -332,6 +332,7 @@ model Setting { disallowSignUp Boolean? @map("disallow_sign_up") disallowSpaceCreation Boolean? @map("disallow_space_creation") disallowSpaceInvitation Boolean? @map("disallow_space_invitation") + aiConfig String? @map("ai_config") @@map("setting") } diff --git a/packages/openapi/src/admin/setting/get.ts b/packages/openapi/src/admin/setting/get.ts index d4bbfb6176..16f0e73fea 100644 --- a/packages/openapi/src/admin/setting/get.ts +++ b/packages/openapi/src/admin/setting/get.ts @@ -2,12 +2,14 @@ import type { RouteConfig } from '@asteasolutions/zod-to-openapi'; import { z } from 'zod'; import { axios } from '../../axios'; import { registerRoute } from '../../utils'; +import { aiConfigSchema } from './update'; export const settingVoSchema = z.object({ instanceId: z.string(), disallowSignUp: z.boolean().nullable(), disallowSpaceCreation: z.boolean().nullable(), disallowSpaceInvitation: z.boolean().nullable(), + aiConfig: aiConfigSchema.nullable(), }); export type ISettingVo = z.infer; diff --git a/packages/openapi/src/admin/setting/update.ts b/packages/openapi/src/admin/setting/update.ts index 2080264ab8..b869af40c4 100644 --- a/packages/openapi/src/admin/setting/update.ts +++ b/packages/openapi/src/admin/setting/update.ts @@ -3,10 +3,30 @@ import { z } from 'zod'; import { axios } from '../../axios'; import { registerRoute } from '../../utils'; +export const llmProviderSchema = z.object({ + type: z.enum(['openai']).default('openai'), + name: z.string(), + apiKey: z.string().optional(), + baseUrl: z.string().url().optional(), + models: z.string().default(''), +}); + +export type LLMProvider = z.infer; + +export const aiConfigSchema = z.object({ + enable: z.boolean().default(false), + llmProviders: z.array(llmProviderSchema).default([]), + // task preferred model + embeddingModel: z.string().optional(), + translationModel: z.string().optional(), + codingModel: z.string().optional(), +}); + export const updateSettingRoSchema = z.object({ disallowSignUp: z.boolean().optional(), disallowSpaceCreation: z.boolean().optional(), disallowSpaceInvitation: z.boolean().optional(), + aiConfig: aiConfigSchema.optional(), }); export type IUpdateSettingRo = z.infer; From ec070be178dd769bc0ef0cb854cdc1ec8954e877 Mon Sep 17 00:00:00 2001 From: mayneyao Date: Mon, 11 Nov 2024 16:02:22 +0800 Subject: [PATCH 05/20] feat(ssr): inject public config into SSR props --- apps/nextjs-app/src/lib/server-env.ts | 10 ++++++++++ apps/nextjs-app/src/lib/withEnv.ts | 16 ++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/apps/nextjs-app/src/lib/server-env.ts b/apps/nextjs-app/src/lib/server-env.ts index c48bd64cfe..2569151314 100644 --- a/apps/nextjs-app/src/lib/server-env.ts +++ b/apps/nextjs-app/src/lib/server-env.ts @@ -10,6 +10,16 @@ export interface IServerEnv { socialAuthProviders?: string[]; storagePrefix?: string; edition?: string; + + // global settings + globalSettings?: { + disallowSignUp?: boolean; + disallowSpaceCreation?: boolean; + disallowSpaceInvitation?: boolean; + aiConfig?: { + enable: boolean; + }; + }; } export const EnvContext = React.createContext({}); diff --git a/apps/nextjs-app/src/lib/withEnv.ts b/apps/nextjs-app/src/lib/withEnv.ts index c25287943a..1428715207 100644 --- a/apps/nextjs-app/src/lib/withEnv.ts +++ b/apps/nextjs-app/src/lib/withEnv.ts @@ -15,12 +15,27 @@ type GetServerSideProps< D extends PreviewData = PreviewData, > = (context: GetServerSidePropsContext) => Promise>; +async function fetchPublicConfig(context?: GetServerSidePropsContext) { + try { + const protocol = process.env.NODE_ENV === 'development' ? 'http' : 'https'; + const host = context?.req?.headers?.host || 'localhost:3000'; + const url = `${protocol}://${host}/api/admin/setting/public`; + console.log('fetchPublicConfig', url); + const response = await fetch(url); + return await response.json(); + } catch (error) { + console.error('Failed to fetch public config:', error); + return {}; + } +} + // eslint-disable-next-line @typescript-eslint/no-explicit-any export default function withEnv

( handler: GetServerSideProps ): NextGetServerSideProps

{ return async (context: GetServerSidePropsContext) => { const { driver } = parseDsn(process.env.PRISMA_DATABASE_URL as string); + const publicConfig = await fetchPublicConfig(context); const env = omitBy( { driver, @@ -31,6 +46,7 @@ export default function withEnv

( sentryDsn: process.env.SENTRY_DSN, socialAuthProviders: process.env.SOCIAL_AUTH_PROVIDERS?.split(','), storagePrefix: process.env.STORAGE_PREFIX, + globalSettings: publicConfig, }, isUndefined ); From e39c5c43e4abf26068a702f3327d986d91086c37 Mon Sep 17 00:00:00 2001 From: mayneyao Date: Mon, 11 Nov 2024 16:05:56 +0800 Subject: [PATCH 06/20] feat(ai): global ai config --- .../app/blocks/admin/setting/SettingPage.tsx | 82 +++--- .../setting/components/ai-config/ai-form.tsx | 196 ++++++++++++++ .../components/ai-config/ai-model-select.tsx | 80 ++++++ .../setting/components/ai-config/hooks.ts | 5 + .../ai-config/llm-provider-manage.tsx | 61 +++++ .../ai-config/new-llm-provider-form.tsx | 245 ++++++++++++++++++ .../common-i18n/src/locales/en/common.json | 33 ++- 7 files changed, 667 insertions(+), 35 deletions(-) create mode 100644 apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-form.tsx create mode 100644 apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-model-select.tsx create mode 100644 apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/hooks.ts create mode 100644 apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/llm-provider-manage.tsx create mode 100644 apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/new-llm-provider-form.tsx diff --git a/apps/nextjs-app/src/features/app/blocks/admin/setting/SettingPage.tsx b/apps/nextjs-app/src/features/app/blocks/admin/setting/SettingPage.tsx index 4d9427fc70..ccdff5b4e1 100644 --- a/apps/nextjs-app/src/features/app/blocks/admin/setting/SettingPage.tsx +++ b/apps/nextjs-app/src/features/app/blocks/admin/setting/SettingPage.tsx @@ -4,6 +4,7 @@ import { getSetting, updateSetting } from '@teable/openapi'; import { Label, Switch } from '@teable/ui-lib/shadcn'; import { useTranslation } from 'next-i18next'; import { CopyInstance } from './components'; +import { AIConfigForm } from './components/ai-config/ai-form'; export interface ISettingPageProps { settingServerData?: ISettingVo; @@ -26,7 +27,7 @@ export const SettingPage = (props: ISettingPageProps) => { }, }); - const onCheckedChange = (key: string, value: boolean) => { + const onValueChange = (key: string, value: unknown) => { mutateUpdateSetting({ [key]: value }); }; @@ -41,48 +42,61 @@ export const SettingPage = (props: ISettingPageProps) => {

{t('admin.setting.description')}
-
-
-
- -
- {t('admin.setting.allowSignUpDescription')} + {/* General Settings Section */} +
+

{t('admin.setting.generalSettings')}

+
+
+
+ +
+ {t('admin.setting.allowSignUpDescription')} +
+ onValueChange('disallowSignUp', !checked)} + />
- onCheckedChange('disallowSignUp', !checked)} - /> -
-
-
- -
- {t('admin.setting.allowSpaceInvitationDescription')} +
+
+ +
+ {t('admin.setting.allowSpaceInvitationDescription')} +
+ onValueChange('disallowSpaceInvitation', !checked)} + />
- onCheckedChange('disallowSpaceInvitation', !checked)} - /> -
-
-
- -
- {t('admin.setting.allowSpaceCreationDescription')} +
+
+ +
+ {t('admin.setting.allowSpaceCreationDescription')} +
+ onValueChange('disallowSpaceCreation', !checked)} + />
- onCheckedChange('disallowSpaceCreation', !checked)} - />
+ {/* AI Configuration Section */} +
+

{t('admin.setting.aiSettings')}

+ onValueChange('aiConfig', value)} + /> +
+

{t('settings.setting.version')}: {process.env.NEXT_PUBLIC_BUILD_VERSION} diff --git a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-form.tsx b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-form.tsx new file mode 100644 index 0000000000..20d2e0a980 --- /dev/null +++ b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-form.tsx @@ -0,0 +1,196 @@ +import { zodResolver } from '@hookform/resolvers/zod'; +import type { LLMProvider } from '@teable/openapi/src/admin/setting'; +import { aiConfigSchema } from '@teable/openapi/src/admin/setting'; +import type { ISettingVo } from '@teable/openapi/src/admin/setting/get'; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, + Switch, + toast, +} from '@teable/ui-lib/shadcn'; +import { useEffect, useMemo } from 'react'; +import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; + +import { AIModelSelect } from './ai-model-select'; +import { LLMProviderManage } from './llm-provider-manage'; + +export function AIConfigForm({ + aiConfig, + setAiConfig, +}: { + aiConfig: ISettingVo['aiConfig']; + setAiConfig: (data: NonNullable) => void; +}) { + const defaultValues = useMemo( + () => + aiConfig ?? { + enable: false, + llmProviders: [], + }, + [aiConfig] + ); + + const form = useForm>({ + resolver: zodResolver(aiConfigSchema), + defaultValues: defaultValues, + }); + const models = (form.watch('llmProviders') ?? []).map((provider) => + provider.models.split(',').map((model) => model.trim() + '@' + provider.name) + ); + const { reset } = form; + const { t } = useTranslation(); + + useEffect(() => { + reset(defaultValues); + }, [defaultValues, reset]); + + function onSubmit(data: NonNullable) { + console.log(data); + setAiConfig(data); + // data.token = "sk-**********" + toast({ + title: t('admin.setting.ai.configUpdated'), + }); + } + + function updateProviders(providers: LLMProvider[]) { + form.setValue('llmProviders', providers); + form.trigger('llmProviders'); + onSubmit(form.getValues()); + } + + return ( +

+ + ( + +
+ {t('admin.setting.ai.enable')} + {t('admin.setting.ai.enableDescription')} +
+ + { + field.onChange(checked); + onSubmit(form.getValues()); + }} + /> + +
+ )} + /> + + + {t('admin.setting.ai.provider')} + {t('admin.setting.ai.providerDescription')} + + + ( + + + + + + + )} + /> + + + + + {t('admin.setting.ai.modelPreferences')} + {t('admin.setting.ai.modelPreferencesDescription')} + + + ( + +
+ + {t('admin.setting.ai.translationModel')} + +
+ + { + field.onChange(value); + onSubmit(form.getValues()); + }} + options={models.flat()} + /> + + {/* */} +
+
+ + {t('admin.setting.ai.translationModelDescription')} + + +
+ )} + /> + ( + +
+ {t('admin.setting.ai.codingModel')} +
+ + { + field.onChange(value); + onSubmit(form.getValues()); + }} + options={models.flat()} + /> + + {/* */} +
+
+ {t('admin.setting.ai.codingModelDescription')} + +
+ )} + /> +
+
+ + + ); +} diff --git a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-model-select.tsx b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-model-select.tsx new file mode 100644 index 0000000000..f3a7375566 --- /dev/null +++ b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-model-select.tsx @@ -0,0 +1,80 @@ +'use client'; + +import { Button } from '@teable/ui-lib'; +import { + cn, + Command, + CommandEmpty, + CommandInput, + CommandItem, + CommandList, + Popover, + PopoverContent, + PopoverTrigger, + ScrollArea, +} from '@teable/ui-lib/shadcn'; +import { Check, ChevronsUpDown } from 'lucide-react'; +import * as React from 'react'; + +export function AIModelSelect({ + value = '', + onValueChange: setValue, + size = 'default', + className, + options = [], +}: { + onValueChange: (value: string) => void; + value: string; + size?: 'xs' | 'sm' | 'lg' | 'default' | null | undefined; + className?: string; + options?: string[]; +}) { + const [open, setOpen] = React.useState(false); + const currentModel = options.find((model) => model.toLowerCase() === value.toLowerCase()); + return ( + + + + + + + + No model found. + +
+ + {options.map((model) => ( + { + setValue(model.toLowerCase() === value.toLowerCase() ? '' : model); + setOpen(false); + }} + > + +

{model}

{' '} +
+ ))} +
+
+
+
+
+
+ ); +} diff --git a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/hooks.ts b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/hooks.ts new file mode 100644 index 0000000000..f0bf79e384 --- /dev/null +++ b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/hooks.ts @@ -0,0 +1,5 @@ +export enum TaskType { + Embedding = 'Embedding', + Translation = 'Translation', + Coding = 'Coding', +} diff --git a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/llm-provider-manage.tsx b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/llm-provider-manage.tsx new file mode 100644 index 0000000000..10c3118f84 --- /dev/null +++ b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/llm-provider-manage.tsx @@ -0,0 +1,61 @@ +import type { LLMProvider } from '@teable/openapi/src/admin/setting'; +import { Button } from '@teable/ui-lib/shadcn'; +import { SlidersHorizontalIcon, XIcon } from 'lucide-react'; + +import { NewLLMProviderForm, UpdateLLMProviderForm } from './new-llm-provider-form'; + +interface ILLMProviderManageProps { + value: LLMProvider[]; + onChange: (value: LLMProvider[]) => void; +} + +export const LLMProviderManage = ({ value, onChange }: ILLMProviderManageProps) => { + const handleAdd = (data: LLMProvider) => { + const newData = [...value, data]; + onChange(newData); + }; + + const handleUpdate = (index: number) => (data: LLMProvider) => { + const newData = value.map((provider, i) => (i === index ? data : provider)); + onChange(newData); + }; + const handleRemove = (index: number) => { + const newData = value.filter((_, i) => i !== index); + onChange(newData); + }; + if (value.length === 0) { + return ; + } + return ( +
+
+ {value.map((provider, index) => ( +
+
+ {provider.name} - {provider.type} +
+
+ + + + +
+
+ ))} + +
+
+ ); +}; diff --git a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/new-llm-provider-form.tsx b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/new-llm-provider-form.tsx new file mode 100644 index 0000000000..8a587211f5 --- /dev/null +++ b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/new-llm-provider-form.tsx @@ -0,0 +1,245 @@ +import { zodResolver } from '@hookform/resolvers/zod'; +import type { LLMProvider } from '@teable/openapi/src/admin/setting'; +import { llmProviderSchema } from '@teable/openapi/src/admin/setting'; +import { + Button, + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, + Input, + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@teable/ui-lib/shadcn'; +import { useState } from 'react'; +import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; + +interface ILLMProviderManageProps { + onAdd: (data: LLMProvider) => void; +} + +export const UpdateLLMProviderForm = ({ + value, + onChange, + children, +}: LLMProviderFormProps & { + children?: React.ReactNode; +}) => { + const [open, setOpen] = useState(false); + const { t } = useTranslation(); + const handleChange = (data: LLMProvider) => { + onChange?.(data); + setOpen(false); + }; + return ( + + {children} + + + {t('admin.setting.ai.updateLLMProvider')} + + + + + ); +}; + +export const NewLLMProviderForm = ({ onAdd }: ILLMProviderManageProps) => { + const { t } = useTranslation(); + const [open, setOpen] = useState(false); + const handleAdd = (data: LLMProvider) => { + onAdd(data); + setOpen(false); + }; + return ( + + + + + + + {t('admin.setting.ai.addProvider')} + {t('admin.setting.ai.addProviderDescription')} + + + + + ); +}; + +interface LLMProviderFormProps { + value?: LLMProvider; + onChange?: (value: LLMProvider) => void; + onAdd?: (data: LLMProvider) => void; +} + +export const LLMProviderForm = ({ onAdd, value, onChange }: LLMProviderFormProps) => { + const { t } = useTranslation(); + const form = useForm({ + resolver: zodResolver(llmProviderSchema), + defaultValues: value || { + name: '', + type: 'openai', + apiKey: '', + baseUrl: '', + models: '', + }, + }); + + function onSubmit(data: LLMProvider) { + onChange ? onChange(data) : onAdd?.(data); + } + + function handleSubmit() { + const data = form.getValues(); + onSubmit(data); + } + + // async function getModelList(e: React.MouseEvent) { + // e.preventDefault(); + // const baseUrl = form.getValues('baseUrl'); + // if (!baseUrl) { + // toast.toast({ + // title: t('common.error'), + // description: t('admin.setting.ai.baseUrlRequired'), + // }); + // return; + // } + // const openai = new OpenAI({ + // apiKey: form.getValues('apiKey'), + // baseURL: baseUrl, + // dangerouslyAllowBrowser: true, + // }); + // try { + // const resp = await openai.models.list(); + // const modelIds = resp.data.map((model) => model.id).join(', '); + // form.setValue('models', modelIds); + // // focus on models input + // form.setFocus('models'); + // } catch (error) { + // console.error(error); + // toast.toast({ + // title: t('common.error'), + // description: t('admin.setting.ai.fetchModelListError'), + // }); + // } + // } + + const mode = onChange ? 'Update' : 'Add'; + + return ( +
+ + ( + + {t('admin.setting.ai.name')} + {t('admin.setting.ai.nameDescription')} + + + + + + )} + /> + ( + + {t('admin.setting.ai.providerType')} + + + + + + )} + /> + { + // only show the following fields if the type is openai + form.watch('type') === 'openai' && ( + ( + + {t('admin.setting.ai.baseUrl')} + + + + {t('admin.setting.ai.baseUrlDescription')} + + + )} + /> + ) + } + ( + + {t('admin.setting.ai.apiKey')} + + + + {t('admin.setting.ai.apiKeyDescription')} + + + )} + /> + ( + + {/*
+ {t('admin.setting.ai.models')} + {form.watch('type') === 'openai' && ( + + )} +
*/} + + + + {t('admin.setting.ai.modelsDescription')} + +
+ )} + /> + + + + ); +}; diff --git a/packages/common-i18n/src/locales/en/common.json b/packages/common-i18n/src/locales/en/common.json index 8c1a507011..87c7a8c955 100644 --- a/packages/common-i18n/src/locales/en/common.json +++ b/packages/common-i18n/src/locales/en/common.json @@ -237,7 +237,38 @@ "allowSpaceInvitation": "Allow sending space invitations", "allowSpaceInvitationDescription": "Disabling this option will prevent users other than administrators from inviting others to join spaces. Enabling this option will allow directly invited users to create an account even if new account registration is disabled.", "allowSpaceCreation": "Allow everyone to create new spaces", - "allowSpaceCreationDescription": "Disabling this option will prevent users other than administrators from creating new spaces." + "allowSpaceCreationDescription": "Disabling this option will prevent users other than administrators from creating new spaces.", + "generalSettings": "General settings", + "aiSettings": "AI settings", + "ai": { + "name": "Name", + "nameDescription": "The name of the LLM provider", + "enable": "Enable AI", + "enableDescription": "Enable AI for current instance, all users will be able to use AI features", + "updateLLMProvider": "Update LLM provider", + "addProvider": "Add LLM provider", + "addProviderDescription": "Add a new LLM provider to the list", + "providerType": "Provider type", + "baseUrl": "Base URL", + "apiKey": "API key", + "baseUrlDescription": "The base URL of the LLM provider", + "apiKeyDescription": "The API key of the LLM provider", + "models": "Models", + "modelsDescription": "The models supported by the LLM provider", + "baseUrlRequired": "Base URL is required", + "fetchModelListError": "Failed to fetch model list", + "provider": "LLM provider", + "providerDescription": "The LLM provider to use", + "modelPreferences": "Model preferences", + "modelPreferencesDescription": "The model preferences for the LLM provider", + "embeddingModel": "Embedding model", + "embeddingModelDescription": "The embedding model to use", + "translationModel": "Translation model", + "translationModelDescription": "The translation model to use", + "codingModel": "Coding model", + "codingModelDescription": "The coding model to use", + "configUpdated": "AI config updated" + } } }, "notification": { From e32975c20cebef77d2a5d7290e6de0557425feff Mon Sep 17 00:00:00 2001 From: mayneyao Date: Mon, 11 Nov 2024 16:07:56 +0800 Subject: [PATCH 07/20] fix(ai): enable AI features based on configuration --- .../field-setting/options/FormulaOptions.tsx | 8 +++- .../src/features/app/hooks/useAI.ts | 8 ++++ .../src/components/editor/formula/Editor.tsx | 40 ++++++++++++------- 3 files changed, 41 insertions(+), 15 deletions(-) create mode 100644 apps/nextjs-app/src/features/app/hooks/useAI.ts diff --git a/apps/nextjs-app/src/features/app/components/field-setting/options/FormulaOptions.tsx b/apps/nextjs-app/src/features/app/components/field-setting/options/FormulaOptions.tsx index 1b090a52c1..97c50398b0 100644 --- a/apps/nextjs-app/src/features/app/components/field-setting/options/FormulaOptions.tsx +++ b/apps/nextjs-app/src/features/app/components/field-setting/options/FormulaOptions.tsx @@ -13,6 +13,7 @@ import { Dialog, DialogContent, DialogTrigger } from '@teable/ui-lib/shadcn'; import { isEmpty, isEqual, keyBy } from 'lodash'; import { useTranslation } from 'next-i18next'; import { useCallback, useMemo, useState } from 'react'; +import { useAI } from '@/features/app/hooks/useAI'; import { TimeZoneFormatting } from '../formatting/TimeZoneFormatting'; import { UnionFormatting } from '../formatting/UnionFormatting'; import { UnionShowAs } from '../show-as/UnionShowAs'; @@ -34,6 +35,7 @@ export const FormulaOptionsInner = (props: { onChange?: (options: Partial) => void; }) => { const { options = {}, onChange } = props; + const { enable: enableAI } = useAI(); const { expression, formatting, showAs } = options; const fields = useFields({ withHidden: true, withDenied: true }); const [visible, setVisible] = useState(false); @@ -119,7 +121,11 @@ export const FormulaOptionsInner = (props: { closeable className="flex size-auto max-w-full overflow-hidden rounded-sm p-0 outline-0 md:w-auto" > - +
diff --git a/apps/nextjs-app/src/features/app/hooks/useAI.ts b/apps/nextjs-app/src/features/app/hooks/useAI.ts new file mode 100644 index 0000000000..e9b1495ac3 --- /dev/null +++ b/apps/nextjs-app/src/features/app/hooks/useAI.ts @@ -0,0 +1,8 @@ +import { useEnv } from './useEnv'; + +export function useAI() { + const env = useEnv(); + return { + enable: env.globalSettings?.aiConfig?.enable, + }; +} diff --git a/packages/sdk/src/components/editor/formula/Editor.tsx b/packages/sdk/src/components/editor/formula/Editor.tsx index 0595da6bbd..17647eae92 100644 --- a/packages/sdk/src/components/editor/formula/Editor.tsx +++ b/packages/sdk/src/components/editor/formula/Editor.tsx @@ -35,14 +35,16 @@ import type { } from './interface'; import { SuggestionItemType } from './interface'; import { FormulaNodePathVisitor } from './visitor'; +import { AlertCircle } from 'lucide-react'; interface IFormulaEditorProps { expression?: string; onConfirm?: (expression: string) => void; + enableAI?: boolean; } export const FormulaEditor: FC = (props) => { - const { expression, onConfirm } = props; + const { expression, onConfirm, enableAI } = props; const fields = useFields({ withHidden: true, withDenied: true }); const { resolvedTheme } = useTheme(); const { t } = useTranslation(); @@ -66,7 +68,7 @@ export const FormulaEditor: FC = (props) => { [formulaFunctionsMap] ); const functionsDisplayMap = useFunctionsDisplayMap(); - const { generateAIResponse, text, loading } = useAIStream(); + const { generateAIResponse, text, loading, error } = useAIStream(); useEffect(() => { if (text) { @@ -355,17 +357,27 @@ export const FormulaEditor: FC = (props) => {

{t('editor.formula.title')}

- + {enableAI && ( +
+ + {error && ( +
+ + {error} +
+ )} +
+ )}
@@ -376,7 +388,7 @@ export const FormulaEditor: FC = (props) => { extensions={extensions} onChange={onValueChange} onSelectionChange={onSelectionChange} - placeholder={t('editor.formula.placeholder')} + placeholder={enableAI ? t('editor.formula.placeholder') : undefined} />
{errMsg}
From f13518fb7b3fea6a7e5d23451a1d25f6d8ecabc3 Mon Sep 17 00:00:00 2001 From: mayneyao Date: Mon, 25 Nov 2024 18:24:18 +0800 Subject: [PATCH 08/20] fix(ai): get model config from settings --- .../src/features/ai/ai.controller.ts | 6 +-- .../src/features/ai/ai.service.ts | 50 ++++++++++++++++--- .../src/features/app/hooks/useAI.ts | 11 ++-- apps/nextjs-app/src/lib/withEnv.ts | 16 ------ packages/openapi/src/admin/setting/get.ts | 31 ++++++++++++ 5 files changed, 84 insertions(+), 30 deletions(-) diff --git a/apps/nestjs-backend/src/features/ai/ai.controller.ts b/apps/nestjs-backend/src/features/ai/ai.controller.ts index 1d11c157ea..12c6fc2364 100644 --- a/apps/nestjs-backend/src/features/ai/ai.controller.ts +++ b/apps/nestjs-backend/src/features/ai/ai.controller.ts @@ -1,13 +1,13 @@ import { Body, Controller, Post, Res } from '@nestjs/common'; import { Response } from 'express'; -import { AiService } from './ai.service'; +import { AiService, Task } from './ai.service'; @Controller('api/ai') export class AiController { constructor(private readonly aiService: AiService) {} @Post() - async generate(@Body('prompt') prompt: string, @Res() res: Response) { - const result = await this.aiService.generate(prompt); + async generate(@Body('prompt') prompt: string, @Body('task') task: Task, @Res() res: Response) { + const result = await this.aiService.generate(prompt, task); result.pipeTextStreamToResponse(res); } } diff --git a/apps/nestjs-backend/src/features/ai/ai.service.ts b/apps/nestjs-backend/src/features/ai/ai.service.ts index 4d4342e88b..784ea1b47a 100644 --- a/apps/nestjs-backend/src/features/ai/ai.service.ts +++ b/apps/nestjs-backend/src/features/ai/ai.service.ts @@ -1,21 +1,55 @@ import { createOpenAI } from '@ai-sdk/openai'; import { Injectable } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; +import { SettingService } from '../setting/setting.service'; import { streamText } from 'ai'; +export enum Task { + Translation = 'translation', + Coding = 'coding', +} + @Injectable() export class AiService { - constructor(private readonly configService: ConfigService) {} + constructor(private readonly settingService: SettingService) {} + + static taskModelMap = { + [Task.Coding]: 'codingModel', + [Task.Translation]: 'translationModel', + }; + + private async getModelConfig(task: Task) { + const { aiConfig } = await this.settingService.getSetting(); + // aiConfig?.codingModel model@provider + const currentTaskModel = AiService.taskModelMap[task]; + const [model, provider] = + (aiConfig?.[currentTaskModel as keyof typeof aiConfig] as string)?.split('@') || []; + const llmProviders = aiConfig?.llmProviders || []; + + const providerConfig = llmProviders.find( + (p) => p.name.toLowerCase() === provider.toLowerCase() + ); + + if (!providerConfig) { + throw new Error('AI provider configuration is not set'); + } + + return { model, baseUrl: providerConfig.baseUrl, apiKey: providerConfig.apiKey }; + } + + async generate(prompt: string, task: Task = Task.Coding) { + const { baseUrl, apiKey, model } = await this.getModelConfig(task); + + if (!baseUrl || !apiKey) { + throw new Error('AI configuration is not set'); + } - async generate(prompt: string) { - const openAIBaseUrl = this.configService.get('OPENAI_BASE_URL'); - const openaiApiKey = this.configService.get('OPENAI_API_KEY'); const openai = createOpenAI({ - baseURL: openAIBaseUrl, - apiKey: openaiApiKey, + baseURL: baseUrl, + apiKey, }); + return await streamText({ - model: openai('gpt-4o-mini'), + model: openai(model), prompt: prompt, }); } diff --git a/apps/nextjs-app/src/features/app/hooks/useAI.ts b/apps/nextjs-app/src/features/app/hooks/useAI.ts index e9b1495ac3..12d40baad9 100644 --- a/apps/nextjs-app/src/features/app/hooks/useAI.ts +++ b/apps/nextjs-app/src/features/app/hooks/useAI.ts @@ -1,8 +1,13 @@ -import { useEnv } from './useEnv'; +import { useQuery } from '@tanstack/react-query'; +import { getPublicSetting } from '@teable/openapi'; export function useAI() { - const env = useEnv(); + const { data } = useQuery({ + queryKey: ['public-ai-config'], + queryFn: () => getPublicSetting().then(({ data }) => data), + }); + return { - enable: env.globalSettings?.aiConfig?.enable, + enable: data?.aiConfig?.enable ?? false, }; } diff --git a/apps/nextjs-app/src/lib/withEnv.ts b/apps/nextjs-app/src/lib/withEnv.ts index 1428715207..c25287943a 100644 --- a/apps/nextjs-app/src/lib/withEnv.ts +++ b/apps/nextjs-app/src/lib/withEnv.ts @@ -15,27 +15,12 @@ type GetServerSideProps< D extends PreviewData = PreviewData, > = (context: GetServerSidePropsContext) => Promise>; -async function fetchPublicConfig(context?: GetServerSidePropsContext) { - try { - const protocol = process.env.NODE_ENV === 'development' ? 'http' : 'https'; - const host = context?.req?.headers?.host || 'localhost:3000'; - const url = `${protocol}://${host}/api/admin/setting/public`; - console.log('fetchPublicConfig', url); - const response = await fetch(url); - return await response.json(); - } catch (error) { - console.error('Failed to fetch public config:', error); - return {}; - } -} - // eslint-disable-next-line @typescript-eslint/no-explicit-any export default function withEnv

( handler: GetServerSideProps ): NextGetServerSideProps

{ return async (context: GetServerSidePropsContext) => { const { driver } = parseDsn(process.env.PRISMA_DATABASE_URL as string); - const publicConfig = await fetchPublicConfig(context); const env = omitBy( { driver, @@ -46,7 +31,6 @@ export default function withEnv

( sentryDsn: process.env.SENTRY_DSN, socialAuthProviders: process.env.SOCIAL_AUTH_PROVIDERS?.split(','), storagePrefix: process.env.STORAGE_PREFIX, - globalSettings: publicConfig, }, isUndefined ); diff --git a/packages/openapi/src/admin/setting/get.ts b/packages/openapi/src/admin/setting/get.ts index 16f0e73fea..931ba689ee 100644 --- a/packages/openapi/src/admin/setting/get.ts +++ b/packages/openapi/src/admin/setting/get.ts @@ -37,3 +37,34 @@ export const GetSettingRoute: RouteConfig = registerRoute({ export const getSetting = async () => { return axios.get(GET_SETTING); }; + +const publicAiConfigSchema = z.object({ + enable: z.boolean(), +}); + +export const publicSettingVoSchema = z.object({ + aiConfig: publicAiConfigSchema.nullable(), +}); +export type IPublicSettingVo = z.infer; + +export const GET_PUBLIC_SETTING = '/admin/setting/public'; +export const GetPublicSettingRoute: RouteConfig = registerRoute({ + method: 'get', + path: GET_PUBLIC_SETTING, + description: 'Get the public instance settings', + request: {}, + responses: { + 200: { + description: 'Returns the public instance settings.', + content: { + 'application/json': { + schema: publicSettingVoSchema, + }, + }, + }, + }, +}); + +export const getPublicSetting = async () => { + return axios.get(GET_PUBLIC_SETTING); +}; From 97166f94f2c21c1dd3c025cd1960ff1e47e5c9c5 Mon Sep 17 00:00:00 2001 From: tea artist Date: Fri, 29 Nov 2024 15:35:22 +0800 Subject: [PATCH 09/20] chore: migration --- apps/nestjs-backend/src/features/ai/ai.module.ts | 4 ++-- apps/nestjs-backend/src/features/ai/ai.service.ts | 3 ++- .../src/features/setting/setting.controller.ts | 2 +- apps/nextjs-app/.env.example | 5 ----- .../migrations/20241128112023_add_ai_config/migration.sql | 2 ++ .../migrations/20241128112016_add_ai_config/migration.sql | 2 ++ 6 files changed, 9 insertions(+), 9 deletions(-) create mode 100644 packages/db-main-prisma/prisma/postgres/migrations/20241128112023_add_ai_config/migration.sql create mode 100644 packages/db-main-prisma/prisma/sqlite/migrations/20241128112016_add_ai_config/migration.sql diff --git a/apps/nestjs-backend/src/features/ai/ai.module.ts b/apps/nestjs-backend/src/features/ai/ai.module.ts index 746462ee1d..ef0667441d 100644 --- a/apps/nestjs-backend/src/features/ai/ai.module.ts +++ b/apps/nestjs-backend/src/features/ai/ai.module.ts @@ -1,10 +1,10 @@ import { Module } from '@nestjs/common'; -import { ConfigModule } from '@nestjs/config'; +import { SettingModule } from '../setting/setting.module'; import { AiController } from './ai.controller'; import { AiService } from './ai.service'; @Module({ - imports: [ConfigModule], + imports: [SettingModule], controllers: [AiController], providers: [AiService], }) diff --git a/apps/nestjs-backend/src/features/ai/ai.service.ts b/apps/nestjs-backend/src/features/ai/ai.service.ts index 784ea1b47a..6d682c1a66 100644 --- a/apps/nestjs-backend/src/features/ai/ai.service.ts +++ b/apps/nestjs-backend/src/features/ai/ai.service.ts @@ -1,7 +1,7 @@ import { createOpenAI } from '@ai-sdk/openai'; import { Injectable } from '@nestjs/common'; -import { SettingService } from '../setting/setting.service'; import { streamText } from 'ai'; +import { SettingService } from '../setting/setting.service'; export enum Task { Translation = 'translation', @@ -12,6 +12,7 @@ export enum Task { export class AiService { constructor(private readonly settingService: SettingService) {} + // eslint-disable-next-line @typescript-eslint/naming-convention static taskModelMap = { [Task.Coding]: 'codingModel', [Task.Translation]: 'translationModel', diff --git a/apps/nestjs-backend/src/features/setting/setting.controller.ts b/apps/nestjs-backend/src/features/setting/setting.controller.ts index 84db30c49b..62d2ce4938 100644 --- a/apps/nestjs-backend/src/features/setting/setting.controller.ts +++ b/apps/nestjs-backend/src/features/setting/setting.controller.ts @@ -3,8 +3,8 @@ import type { ISettingVo } from '@teable/openapi'; import { IUpdateSettingRo, updateSettingRoSchema } from '@teable/openapi'; import { ZodValidationPipe } from '../../zod.validation.pipe'; import { Permissions } from '../auth/decorators/permissions.decorator'; -import { SettingService } from './setting.service'; import { Public } from '../auth/decorators/public.decorator'; +import { SettingService } from './setting.service'; @Controller('api/admin/setting') export class SettingController { diff --git a/apps/nextjs-app/.env.example b/apps/nextjs-app/.env.example index ba6526b7ff..9c7671d55a 100644 --- a/apps/nextjs-app/.env.example +++ b/apps/nextjs-app/.env.example @@ -164,8 +164,3 @@ NEXT_BUILD_ENV_SENTRY_DEBUG=false NEXT_BUILD_ENV_SENTRY_TRACING=false # enable nextjs image optimization NEXT_ENV_IMAGES_ALL_REMOTE=true - -# openai api key -OPENAI_API_KEY="xxxxxxxxx" -# openai base url -OPENAI_BASE_URL="xxxxxxxxx" diff --git a/packages/db-main-prisma/prisma/postgres/migrations/20241128112023_add_ai_config/migration.sql b/packages/db-main-prisma/prisma/postgres/migrations/20241128112023_add_ai_config/migration.sql new file mode 100644 index 0000000000..0e9a0d2ed6 --- /dev/null +++ b/packages/db-main-prisma/prisma/postgres/migrations/20241128112023_add_ai_config/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "setting" ADD COLUMN "ai_config" TEXT; diff --git a/packages/db-main-prisma/prisma/sqlite/migrations/20241128112016_add_ai_config/migration.sql b/packages/db-main-prisma/prisma/sqlite/migrations/20241128112016_add_ai_config/migration.sql new file mode 100644 index 0000000000..1512c46ace --- /dev/null +++ b/packages/db-main-prisma/prisma/sqlite/migrations/20241128112016_add_ai_config/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "setting" ADD COLUMN "ai_config" TEXT; From 699ea4896021c0193d0561e6fe158c43167f6c31 Mon Sep 17 00:00:00 2001 From: SkyHuang Date: Fri, 27 Dec 2024 12:27:32 +0800 Subject: [PATCH 10/20] feat: adapt to multiple LLM providers --- apps/nestjs-backend/package.json | 7 +- .../src/features/ai/ai.controller.ts | 3 +- .../src/features/ai/ai.module.ts | 1 + .../src/features/ai/ai.service.ts | 62 ++- .../src/features/ai/constant.ts | 8 + apps/nestjs-backend/src/features/ai/type.ts | 5 + .../setting/components/ai-config/ai-form.tsx | 42 +- .../components/ai-config/ai-model-select.tsx | 19 +- .../setting/components/ai-config/constant.ts | 40 ++ .../setting/components/ai-config/hooks.ts | 5 - .../ai-config/llm-provider-manage.tsx | 2 +- .../ai-config/new-llm-provider-form.tsx | 113 ++--- packages/openapi/src/admin/setting/update.ts | 11 +- packages/sdk/src/hooks/use-ai.ts | 81 ++-- pnpm-lock.yaml | 391 ++++++++++-------- 15 files changed, 464 insertions(+), 326 deletions(-) create mode 100644 apps/nestjs-backend/src/features/ai/constant.ts create mode 100644 apps/nestjs-backend/src/features/ai/type.ts create mode 100644 apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/constant.ts delete mode 100644 apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/hooks.ts diff --git a/apps/nestjs-backend/package.json b/apps/nestjs-backend/package.json index 8b1bdaa0d4..f919f8526f 100644 --- a/apps/nestjs-backend/package.json +++ b/apps/nestjs-backend/package.json @@ -109,7 +109,12 @@ "webpack": "5.91.0" }, "dependencies": { - "@ai-sdk/openai": "0.0.72", + "@ai-sdk/anthropic": "1.0.6", + "@ai-sdk/azure": "1.0.13", + "@ai-sdk/cohere": "1.0.6", + "@ai-sdk/google": "1.0.12", + "@ai-sdk/mistral": "1.0.6", + "@ai-sdk/openai": "1.0.11", "@aws-sdk/client-s3": "3.609.0", "@aws-sdk/s3-request-presigner": "3.609.0", "@keyv/redis": "2.8.4", diff --git a/apps/nestjs-backend/src/features/ai/ai.controller.ts b/apps/nestjs-backend/src/features/ai/ai.controller.ts index 12c6fc2364..87d4ca5b65 100644 --- a/apps/nestjs-backend/src/features/ai/ai.controller.ts +++ b/apps/nestjs-backend/src/features/ai/ai.controller.ts @@ -1,6 +1,7 @@ import { Body, Controller, Post, Res } from '@nestjs/common'; import { Response } from 'express'; -import { AiService, Task } from './ai.service'; +import { AiService } from './ai.service'; +import { Task } from './type'; @Controller('api/ai') export class AiController { diff --git a/apps/nestjs-backend/src/features/ai/ai.module.ts b/apps/nestjs-backend/src/features/ai/ai.module.ts index ef0667441d..4dbecc4c87 100644 --- a/apps/nestjs-backend/src/features/ai/ai.module.ts +++ b/apps/nestjs-backend/src/features/ai/ai.module.ts @@ -7,5 +7,6 @@ import { AiService } from './ai.service'; imports: [SettingModule], controllers: [AiController], providers: [AiService], + exports: [AiService], }) export class AiModule {} diff --git a/apps/nestjs-backend/src/features/ai/ai.service.ts b/apps/nestjs-backend/src/features/ai/ai.service.ts index 6d682c1a66..0be84a1cd5 100644 --- a/apps/nestjs-backend/src/features/ai/ai.service.ts +++ b/apps/nestjs-backend/src/features/ai/ai.service.ts @@ -1,27 +1,33 @@ +import { createAnthropic } from '@ai-sdk/anthropic'; +import { createAzure } from '@ai-sdk/azure'; +import { createCohere } from '@ai-sdk/cohere'; +import { createGoogleGenerativeAI } from '@ai-sdk/google'; +import { createMistral } from '@ai-sdk/mistral'; import { createOpenAI } from '@ai-sdk/openai'; import { Injectable } from '@nestjs/common'; +import { LLMProviderType } from '@teable/openapi'; import { streamText } from 'ai'; import { SettingService } from '../setting/setting.service'; - -export enum Task { - Translation = 'translation', - Coding = 'coding', -} +import { TASK_MODEL_MAP } from './constant'; +import { Task } from './type'; @Injectable() export class AiService { constructor(private readonly settingService: SettingService) {} - // eslint-disable-next-line @typescript-eslint/naming-convention - static taskModelMap = { - [Task.Coding]: 'codingModel', - [Task.Translation]: 'translationModel', - }; + readonly modelProviders = { + [LLMProviderType.OPENAI]: createOpenAI, + [LLMProviderType.ANTHROPIC]: createAnthropic, + [LLMProviderType.GOOGLE]: createGoogleGenerativeAI, + [LLMProviderType.AZURE]: createAzure, + [LLMProviderType.COHERE]: createCohere, + [LLMProviderType.MISTRAL]: createMistral, + } as const; - private async getModelConfig(task: Task) { + async getModelConfig(task: Task) { const { aiConfig } = await this.settingService.getSetting(); // aiConfig?.codingModel model@provider - const currentTaskModel = AiService.taskModelMap[task]; + const currentTaskModel = TASK_MODEL_MAP[task]; const [model, provider] = (aiConfig?.[currentTaskModel as keyof typeof aiConfig] as string)?.split('@') || []; const llmProviders = aiConfig?.llmProviders || []; @@ -37,20 +43,36 @@ export class AiService { return { model, baseUrl: providerConfig.baseUrl, apiKey: providerConfig.apiKey }; } - async generate(prompt: string, task: Task = Task.Coding) { - const { baseUrl, apiKey, model } = await this.getModelConfig(task); + async getModelInstance( + task: Task + ): Promise< + ReturnType> + > { + const config = await this.getModelConfig(task); - if (!baseUrl || !apiKey) { + if (!config.baseUrl || !config.apiKey) { throw new Error('AI configuration is not set'); } - const openai = createOpenAI({ - baseURL: baseUrl, - apiKey, - }); + const provider = Object.entries(this.modelProviders).find(([key]) => + config.model.toLowerCase().includes(key.toLowerCase()) + )?.[1]; + + if (!provider) { + throw new Error(`Unsupported AI provider for model: ${config.model}`); + } + + return provider({ + baseURL: config.baseUrl, + apiKey: config.apiKey, + })(config.model); + } + + async generate(prompt: string, task: Task = Task.Coding) { + const modelInstance = await this.getModelInstance(task); return await streamText({ - model: openai(model), + model: modelInstance, prompt: prompt, }); } diff --git a/apps/nestjs-backend/src/features/ai/constant.ts b/apps/nestjs-backend/src/features/ai/constant.ts new file mode 100644 index 0000000000..89b43e00d7 --- /dev/null +++ b/apps/nestjs-backend/src/features/ai/constant.ts @@ -0,0 +1,8 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { Task } from './type'; + +export const TASK_MODEL_MAP = { + [Task.Coding]: 'codingModel', + [Task.Embedding]: 'embeddingModel', + [Task.Translation]: 'translationModel', +}; diff --git a/apps/nestjs-backend/src/features/ai/type.ts b/apps/nestjs-backend/src/features/ai/type.ts new file mode 100644 index 0000000000..9b5417b40a --- /dev/null +++ b/apps/nestjs-backend/src/features/ai/type.ts @@ -0,0 +1,5 @@ +export enum Task { + Coding = 'coding', + Embedding = 'embedding', + Translation = 'translation', +} diff --git a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-form.tsx b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-form.tsx index 20d2e0a980..0d0e63cbf0 100644 --- a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-form.tsx +++ b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-form.tsx @@ -18,9 +18,9 @@ import { Switch, toast, } from '@teable/ui-lib/shadcn'; +import { useTranslation } from 'next-i18next'; import { useEffect, useMemo } from 'react'; import { useForm } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; import { AIModelSelect } from './ai-model-select'; import { LLMProviderManage } from './llm-provider-manage'; @@ -49,16 +49,14 @@ export function AIConfigForm({ provider.models.split(',').map((model) => model.trim() + '@' + provider.name) ); const { reset } = form; - const { t } = useTranslation(); + const { t } = useTranslation('common'); useEffect(() => { reset(defaultValues); }, [defaultValues, reset]); function onSubmit(data: NonNullable) { - console.log(data); setAiConfig(data); - // data.token = "sk-**********" toast({ title: t('admin.setting.ai.configUpdated'), }); @@ -122,12 +120,15 @@ export function AIConfigForm({ (

- {t('admin.setting.ai.translationModel')} + {t('admin.setting.ai.codingModel')} + + {t('admin.setting.ai.codingModelDescription')} +
@@ -140,29 +141,25 @@ export function AIConfigForm({ options={models.flat()} /> - {/* */}
- - {t('admin.setting.ai.translationModelDescription')} - + )} /> (
- {t('admin.setting.ai.codingModel')} + + {t('admin.setting.ai.embeddingModel')} + + {t('admin.setting.ai.embeddingModelDescription')} + +
- {/* */}
- {t('admin.setting.ai.codingModelDescription')} +
)} diff --git a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-model-select.tsx b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-model-select.tsx index f3a7375566..c1a27b1743 100644 --- a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-model-select.tsx +++ b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-model-select.tsx @@ -14,6 +14,7 @@ import { ScrollArea, } from '@teable/ui-lib/shadcn'; import { Check, ChevronsUpDown } from 'lucide-react'; +import { useTranslation } from 'next-i18next'; import * as React from 'react'; export function AIModelSelect({ @@ -31,6 +32,8 @@ export function AIModelSelect({ }) { const [open, setOpen] = React.useState(false); const currentModel = options.find((model) => model.toLowerCase() === value.toLowerCase()); + const { t } = useTranslation('common'); + return ( @@ -39,17 +42,19 @@ export function AIModelSelect({ role="combobox" aria-expanded={open} size={size} - className={cn('grow justify-between ', className)} + className={cn('grow justify-between', className)} > -

{value ? currentModel : 'Select model...'}

+

+ {currentModel ?? t('admin.setting.ai.selectModel')} +

- + - - No model found. - + + {t('admin.setting.ai.noModelFound')} +
{options.map((model) => ( @@ -67,7 +72,7 @@ export function AIModelSelect({ value.toLowerCase() === model.toLowerCase() ? 'opacity-100' : 'opacity-0' )} /> -

{model}

{' '} +

{model}

{' '} ))}
diff --git a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/constant.ts b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/constant.ts new file mode 100644 index 0000000000..8f9a963406 --- /dev/null +++ b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/constant.ts @@ -0,0 +1,40 @@ +import { LLMProviderType } from '@teable/openapi'; + +export const LLM_PROVIDERS = [ + { + value: LLMProviderType.OPENAI, + label: 'OpenAI', + baseUrlPlaceholder: 'https://api.openai.com/v1', + modelsPlaceholder: 'gpt-4, gpt-4o-mini, gpt-3.5-turbo', + }, + { + value: LLMProviderType.AZURE, + label: 'Azure', + baseUrlPlaceholder: 'https://{your-resource-name}.openai.azure.com', + modelsPlaceholder: 'gpt-4, gpt-35-turbo', + }, + { + value: LLMProviderType.ANTHROPIC, + label: 'Anthropic', + baseUrlPlaceholder: 'https://api.anthropic.com', + modelsPlaceholder: 'claude-3-opus-20240229, claude-3-5-sonnet-20241022', + }, + { + value: LLMProviderType.GOOGLE, + label: 'Google', + baseUrlPlaceholder: 'https://generativelanguage.googleapis.com', + modelsPlaceholder: 'gemini-pro-vision, gemini-1.5-flash-002', + }, + { + value: LLMProviderType.COHERE, + label: 'Cohere', + baseUrlPlaceholder: 'https://api.cohere.ai/v1', + modelsPlaceholder: 'command-r, command-r-plus, command-r-plus-online', + }, + { + value: LLMProviderType.MISTRAL, + label: 'Mistral', + baseUrlPlaceholder: 'https://api.mistral.ai/v1', + modelsPlaceholder: 'mistral-large-latest, open-mistral-nemo', + }, +] as const; diff --git a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/hooks.ts b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/hooks.ts deleted file mode 100644 index f0bf79e384..0000000000 --- a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/hooks.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum TaskType { - Embedding = 'Embedding', - Translation = 'Translation', - Coding = 'Coding', -} diff --git a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/llm-provider-manage.tsx b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/llm-provider-manage.tsx index 10c3118f84..6dc579fd8d 100644 --- a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/llm-provider-manage.tsx +++ b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/llm-provider-manage.tsx @@ -28,7 +28,7 @@ export const LLMProviderManage = ({ value, onChange }: ILLMProviderManageProps) } return (
-
+
{value.map((provider, index) => (
void; @@ -39,7 +41,7 @@ export const UpdateLLMProviderForm = ({ children?: React.ReactNode; }) => { const [open, setOpen] = useState(false); - const { t } = useTranslation(); + const { t } = useTranslation('common'); const handleChange = (data: LLMProvider) => { onChange?.(data); setOpen(false); @@ -94,7 +96,7 @@ export const LLMProviderForm = ({ onAdd, value, onChange }: LLMProviderFormProps resolver: zodResolver(llmProviderSchema), defaultValues: value || { name: '', - type: 'openai', + type: LLMProviderType.OPENAI, apiKey: '', baseUrl: '', models: '', @@ -141,6 +143,8 @@ export const LLMProviderForm = ({ onAdd, value, onChange }: LLMProviderFormProps // } const mode = onChange ? 'Update' : 'Add'; + const type = form.watch('type'); + const currentProvider = LLM_PROVIDERS.find((provider) => provider.value === type); return (
@@ -149,14 +153,12 @@ export const LLMProviderForm = ({ onAdd, value, onChange }: LLMProviderFormProps name="name" render={({ field }) => ( - {t('admin.setting.ai.name')} - {t('admin.setting.ai.nameDescription')} +
+ {t('admin.setting.ai.name')} + {t('admin.setting.ai.nameDescription')} +
- +
@@ -178,8 +180,11 @@ export const LLMProviderForm = ({ onAdd, value, onChange }: LLMProviderFormProps - OpenAI - {/* Google */} + {LLM_PROVIDERS.map((provider) => ( + + {provider.label} + + ))} @@ -187,58 +192,56 @@ export const LLMProviderForm = ({ onAdd, value, onChange }: LLMProviderFormProps )} /> - { - // only show the following fields if the type is openai - form.watch('type') === 'openai' && ( + {!!currentProvider && ( + <> ( - {t('admin.setting.ai.baseUrl')} +
+ {t('admin.setting.ai.baseUrl')} + {t('admin.setting.ai.baseUrlDescription')} +
- + - {t('admin.setting.ai.baseUrlDescription')}
)} /> - ) - } - ( - - {t('admin.setting.ai.apiKey')} - - - - {t('admin.setting.ai.apiKeyDescription')} - - - )} - /> - ( - - {/*
- {t('admin.setting.ai.models')} - {form.watch('type') === 'openai' && ( - - )} -
*/} - - - - {t('admin.setting.ai.modelsDescription')} - -
- )} - /> - + ( + +
+ {t('admin.setting.ai.apiKey')} + {t('admin.setting.ai.apiKeyDescription')} +
+ + + + +
+ )} + /> + ( + +
+ {t('admin.setting.ai.models')} + {t('admin.setting.ai.modelsDescription')} +
+ + + + +
+ )} + /> + + + )} ); diff --git a/packages/openapi/src/admin/setting/update.ts b/packages/openapi/src/admin/setting/update.ts index b869af40c4..73826618a0 100644 --- a/packages/openapi/src/admin/setting/update.ts +++ b/packages/openapi/src/admin/setting/update.ts @@ -3,8 +3,17 @@ import { z } from 'zod'; import { axios } from '../../axios'; import { registerRoute } from '../../utils'; +export enum LLMProviderType { + OPENAI = 'openai', + ANTHROPIC = 'anthropic', + GOOGLE = 'google', + AZURE = 'azure', + COHERE = 'cohere', + MISTRAL = 'mistral', +} + export const llmProviderSchema = z.object({ - type: z.enum(['openai']).default('openai'), + type: z.nativeEnum(LLMProviderType), name: z.string(), apiKey: z.string().optional(), baseUrl: z.string().url().optional(), diff --git a/packages/sdk/src/hooks/use-ai.ts b/packages/sdk/src/hooks/use-ai.ts index 8b600811a5..da04cf89d9 100644 --- a/packages/sdk/src/hooks/use-ai.ts +++ b/packages/sdk/src/hooks/use-ai.ts @@ -13,52 +13,55 @@ export const useAIStream = (options?: IUseAIStreamOptions) => { const [error, setError] = useState(null); const controllerRef = useRef(null); - const generateAIResponse = useCallback(async (prompt: string) => { - setText(''); - setError(null); - setLoading(true); + const generateAIResponse = useCallback( + async (prompt: string) => { + setText(''); + setError(null); + setLoading(true); - controllerRef.current = new AbortController(); - const timeoutId = setTimeout(() => controllerRef.current?.abort(), timeout); + controllerRef.current = new AbortController(); + const timeoutId = setTimeout(() => controllerRef.current?.abort(), timeout); - try { - const result = await fetch(aiApiEndpoint, { - method: 'POST', - headers: { - // eslint-disable-next-line @typescript-eslint/naming-convention - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ prompt }), - signal: controllerRef.current.signal, - }); + try { + const result = await fetch(aiApiEndpoint, { + method: 'POST', + headers: { + // eslint-disable-next-line @typescript-eslint/naming-convention + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ prompt }), + signal: controllerRef.current.signal, + }); - if (!result.ok) { - throw new Error(`HTTP error! status: ${result.status}`); - } + if (!result.ok) { + throw new Error(`HTTP error! status: ${result.status}`); + } - const reader = result.body?.getReader(); - if (!reader) throw new Error('No reader available'); + const reader = result.body?.getReader(); + if (!reader) throw new Error('No reader available'); - let reading = true; - while (reading) { - const { done, value } = await reader.read(); - if (done) { - reading = false; - break; - } + let reading = true; + while (reading) { + const { done, value } = await reader.read(); + if (done) { + reading = false; + break; + } - const chunk = new TextDecoder().decode(value); - setText((prev) => prev + chunk); + const chunk = new TextDecoder().decode(value); + setText((prev) => prev + chunk); + } + } catch (error) { + const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + setError(errorMessage); + console.error('Error streaming AI response:', error); + } finally { + clearTimeout(timeoutId); + setLoading(false); } - } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error'; - setError(errorMessage); - console.error('Error streaming AI response:', error); - } finally { - clearTimeout(timeoutId); - setLoading(false); - } - }, []); + }, + [timeout] + ); const stop = useCallback(() => { controllerRef.current?.abort(); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5917758d50..639dc909d2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -57,9 +57,24 @@ importers: apps/nestjs-backend: dependencies: + '@ai-sdk/anthropic': + specifier: 1.0.6 + version: 1.0.6(zod@3.22.4) + '@ai-sdk/azure': + specifier: 1.0.13 + version: 1.0.13(zod@3.22.4) + '@ai-sdk/cohere': + specifier: 1.0.6 + version: 1.0.6(zod@3.22.4) + '@ai-sdk/google': + specifier: 1.0.12 + version: 1.0.12(zod@3.22.4) + '@ai-sdk/mistral': + specifier: 1.0.6 + version: 1.0.6(zod@3.22.4) '@ai-sdk/openai': - specifier: 0.0.72 - version: 0.0.72(zod@3.22.4) + specifier: 1.0.11 + version: 1.0.11(zod@3.22.4) '@aws-sdk/client-s3': specifier: 3.609.0 version: 3.609.0 @@ -1146,7 +1161,7 @@ importers: version: 9.1.0(eslint@8.57.0) eslint-import-resolver-typescript: specifier: 3.6.1 - version: 3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-plugin-import@2.29.1)(eslint@8.57.0) + version: 3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) eslint-plugin-import: specifier: 2.29.1 version: 2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) @@ -1627,7 +1642,7 @@ importers: version: 5.4.3 vite-plugin-svgr: specifier: 4.2.0 - version: 4.2.0(rollup@2.79.2)(typescript@5.4.3)(vite@5.4.11(@types/node@20.9.0)(terser@5.37.0)) + version: 4.2.0(rollup@4.28.1)(typescript@5.4.3)(vite@5.4.11(@types/node@20.9.0)(terser@5.37.0)) vite-tsconfig-paths: specifier: 4.3.2 version: 4.3.2(typescript@5.4.3)(vite@5.4.11(@types/node@20.9.0)(terser@5.37.0)) @@ -1931,13 +1946,13 @@ importers: version: 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-numfmt@0.3.0)(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/design': specifier: 0.3.0 - version: 0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/docs': specifier: 0.3.0 version: 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/docs-ui': specifier: 0.3.0 - version: 0.3.0(mlkoehwl22dn7dps2b2ws3yr64) + version: 0.3.0(a3jyoob4q43zypt3aqzliehg5u) '@univerjs/engine-formula': specifier: 0.3.0 version: 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) @@ -1946,22 +1961,22 @@ importers: version: 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/facade': specifier: 0.3.0 - version: 0.3.0(gkf3r7efwe4ghpx7bkb6bg5i2u) + version: 0.3.0(imy6orjkbvu6zl5y3h5o7cj2ii) '@univerjs/sheets': specifier: 0.3.0 version: 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-numfmt@0.3.0)(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/sheets-data-validation': specifier: 0.3.0 - version: 0.3.0(lcc6z77ydbyglikggl6hma4mzu) + version: 0.3.0(viupcfj3lp6rcxf3nnaburzcbq) '@univerjs/sheets-formula': specifier: 0.3.0 - version: 0.3.0(4pw4zmvgvh6humise77ns5t3we) + version: 0.3.0(qx6hfmzvsn7yy2npxnlfsjh5ia) '@univerjs/sheets-ui': specifier: 0.3.0 - version: 0.3.0(v4qlldkypixlkfvwhtaclquoh4) + version: 0.3.0(nzhnn66wgwj324iz25zuis64ie) '@univerjs/ui': specifier: 0.3.0 - version: 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) + version: 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) i18next: specifier: 23.10.1 version: 23.10.1 @@ -2050,8 +2065,38 @@ packages: '@adobe/css-tools@4.4.1': resolution: {integrity: sha512-12WGKBQzjUAI4ayyF4IAtfw2QR/IDoqk6jTddXDhtYTJF9ASmoE1zst7cVtP0aL/F1jUJL5r+JxKXKEgHNbEUQ==} - '@ai-sdk/openai@0.0.72': - resolution: {integrity: sha512-IKsgxIt6KJGkEHyMp975xW5VPmetwhI8g9H6dDmwvemBB41IRQa78YMNttiJqPcgmrZX2QfErOICv1gQvZ1gZg==} + '@ai-sdk/anthropic@1.0.6': + resolution: {integrity: sha512-/9f/CJfR+9rXRV55KSBExMCGeVV7QE5SlagLniak4j74t4/Zg1Aoa69AHftJ78rz/S5srvAB9YtTu52NY+E0mg==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/azure@1.0.13': + resolution: {integrity: sha512-yLzjGANoCUAB8Q0KBfcBIYiiU81reSkbzl/yBdYuLH34x2Lob9RUUSXMGIPdk6XW5dyJYYF9jZdER/EbVD3Jhg==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/cohere@1.0.6': + resolution: {integrity: sha512-V0WELSgsAWX2XYjNca4a/jxIc/mGMW1qXZIo8aSRfQDRsx4xi1c/Vodfc8YlTE9Jgvl0vi4I5R93ZaJdk0aFMA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/google@1.0.12': + resolution: {integrity: sha512-vZUK8X997tKmycwCa9d26PoGtIyNEILykYb6JscMoA/pfr5Nss8Ox1JtSGn+PRkehpJhclOaLNWV1JQAjp73aA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/mistral@1.0.6': + resolution: {integrity: sha512-MAqMhhYJmXVVxVAg78qsNRmt2eQ7gVhL8ZbCd09fTlapRJ+LyrJ6RgrrU+ZwWb6aaYu9sLbnHZ+thAbPzxaD0Q==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/openai@1.0.11': + resolution: {integrity: sha512-qI9s7Slma5i5bB4yYVlFdcG3PNDwdqivPT1Dr8adDX92nSSpILjgFIooS5yys9sXjvvcfOi/WXbDvVhLSRRlvg==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 @@ -2065,10 +2110,23 @@ packages: zod: optional: true + '@ai-sdk/provider-utils@2.0.5': + resolution: {integrity: sha512-2M7vLhYN0ThGjNlzow7oO/lsL+DyMxvGMIYmVQvEYaCWhDzxH5dOp78VNjJIVwHzVLMbBDigX3rJuzAs853idw==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + peerDependenciesMeta: + zod: + optional: true + '@ai-sdk/provider@0.0.26': resolution: {integrity: sha512-dQkfBDs2lTYpKM8389oopPdQgIU007GQyCbuPPrV+K6MtSII3HBfE0stUIMXUb44L+LK1t6GXPP7wjSzjO6uKg==} engines: {node: '>=18'} + '@ai-sdk/provider@1.0.3': + resolution: {integrity: sha512-WiuJEpHTrltOIzv3x2wx4gwksAHW0h6nK3SoDzjqCOJLu/2OJ1yASESTIX+f07ChFykHElVoP80Ol/fe9dw6tQ==} + engines: {node: '>=18'} + '@ai-sdk/react@0.0.70': resolution: {integrity: sha512-GnwbtjW4/4z7MleLiW+TOZC2M29eCg1tOUpuEiYFMmFNZK8mkrqM0PFZMo6UsYeUYMWqEOOcPOU9OQVJMJh7IQ==} engines: {node: '>=18'} @@ -10540,6 +10598,10 @@ packages: resolution: {integrity: sha512-v0eOBUbiaFojBu2s2NPBfYUoRR9GjcDNvCXVaqEf5vVfpIAh9f8RCo4vXTP8c63QRKCFwoLpMpTdPwwhEKVgzA==} engines: {node: '>=14.18'} + eventsource-parser@3.0.0: + resolution: {integrity: sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA==} + engines: {node: '>=18.0.0'} + evp_bytestokey@1.0.3: resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} @@ -13170,6 +13232,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + nanoid@5.0.7: resolution: {integrity: sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==} engines: {node: ^18 || >=20} @@ -17457,10 +17524,41 @@ snapshots: '@adobe/css-tools@4.4.1': {} - '@ai-sdk/openai@0.0.72(zod@3.22.4)': + '@ai-sdk/anthropic@1.0.6(zod@3.22.4)': dependencies: - '@ai-sdk/provider': 0.0.26 - '@ai-sdk/provider-utils': 1.0.22(zod@3.22.4) + '@ai-sdk/provider': 1.0.3 + '@ai-sdk/provider-utils': 2.0.5(zod@3.22.4) + zod: 3.22.4 + + '@ai-sdk/azure@1.0.13(zod@3.22.4)': + dependencies: + '@ai-sdk/openai': 1.0.11(zod@3.22.4) + '@ai-sdk/provider': 1.0.3 + '@ai-sdk/provider-utils': 2.0.5(zod@3.22.4) + zod: 3.22.4 + + '@ai-sdk/cohere@1.0.6(zod@3.22.4)': + dependencies: + '@ai-sdk/provider': 1.0.3 + '@ai-sdk/provider-utils': 2.0.5(zod@3.22.4) + zod: 3.22.4 + + '@ai-sdk/google@1.0.12(zod@3.22.4)': + dependencies: + '@ai-sdk/provider': 1.0.3 + '@ai-sdk/provider-utils': 2.0.5(zod@3.22.4) + zod: 3.22.4 + + '@ai-sdk/mistral@1.0.6(zod@3.22.4)': + dependencies: + '@ai-sdk/provider': 1.0.3 + '@ai-sdk/provider-utils': 2.0.5(zod@3.22.4) + zod: 3.22.4 + + '@ai-sdk/openai@1.0.11(zod@3.22.4)': + dependencies: + '@ai-sdk/provider': 1.0.3 + '@ai-sdk/provider-utils': 2.0.5(zod@3.22.4) zod: 3.22.4 '@ai-sdk/provider-utils@1.0.22(zod@3.22.4)': @@ -17472,10 +17570,23 @@ snapshots: optionalDependencies: zod: 3.22.4 + '@ai-sdk/provider-utils@2.0.5(zod@3.22.4)': + dependencies: + '@ai-sdk/provider': 1.0.3 + eventsource-parser: 3.0.0 + nanoid: 3.3.8 + secure-json-parse: 2.7.0 + optionalDependencies: + zod: 3.22.4 + '@ai-sdk/provider@0.0.26': dependencies: json-schema: 0.4.0 + '@ai-sdk/provider@1.0.3': + dependencies: + json-schema: 0.4.0 + '@ai-sdk/react@0.0.70(react@18.3.1)(zod@3.22.4)': dependencies: '@ai-sdk/provider-utils': 1.0.22(zod@3.22.4) @@ -21996,14 +22107,6 @@ snapshots: optionalDependencies: rollup: 2.78.0 - '@rollup/pluginutils@5.1.4(rollup@2.79.2)': - dependencies: - '@types/estree': 1.0.6 - estree-walker: 2.0.2 - picomatch: 4.0.2 - optionalDependencies: - rollup: 2.79.2 - '@rollup/pluginutils@5.1.4(rollup@4.28.1)': dependencies: '@types/estree': 1.0.6 @@ -24445,36 +24548,6 @@ snapshots: - '@univerjs/engine-numfmt' - '@univerjs/rpc' - '@univerjs/design@0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@rc-component/color-picker': 2.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@rc-component/trigger': 2.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@types/react-mentions': 4.4.1 - '@univerjs/icons': 0.1.87(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - clsx: 2.1.0 - dayjs: 1.11.10 - rc-dialog: 9.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-dropdown: 4.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-input: 1.7.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-input-number: 9.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-menu: 9.16.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-picker: 4.8.3(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-segmented: 2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-select: 14.16.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-textarea: 1.8.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-tooltip: 6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - rc-util: 5.44.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-draggable: 4.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react-grid-layout: 1.4.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react-mentions: 4.4.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - transitivePeerDependencies: - - date-fns - - luxon - - moment - '@univerjs/design@0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@rc-component/color-picker': 2.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -24513,16 +24586,16 @@ snapshots: - react - react-dom - '@univerjs/docs-ui@0.3.0(mlkoehwl22dn7dps2b2ws3yr64)': + '@univerjs/docs-ui@0.3.0(a3jyoob4q43zypt3aqzliehg5u)': dependencies: '@univerjs/core': 0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1) - '@univerjs/design': 0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@univerjs/design': 0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/docs': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/engine-formula': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/engine-render': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/icons': 0.1.87(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) - clsx: 2.1.0 + '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) + clsx: 2.1.1 react: 18.3.1 rxjs: 7.8.1 transitivePeerDependencies: @@ -24534,15 +24607,15 @@ snapshots: '@univerjs/engine-render': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) rxjs: 7.8.1 - '@univerjs/drawing-ui@0.3.0(enqntv6hnjjl7d3w4jlt2ndy3i)': + '@univerjs/drawing-ui@0.3.0(eomp5knungglzd5skzbvl7ekpe)': dependencies: '@univerjs/core': 0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1) - '@univerjs/design': 0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@univerjs/design': 0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/drawing': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1) '@univerjs/engine-render': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/icons': 0.1.87(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) - clsx: 2.1.0 + '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) + clsx: 2.1.1 react: 18.3.1 rxjs: 7.8.1 transitivePeerDependencies: @@ -24578,31 +24651,31 @@ snapshots: opentype.js: 1.3.4 rxjs: 7.8.1 - '@univerjs/facade@0.3.0(gkf3r7efwe4ghpx7bkb6bg5i2u)': + '@univerjs/facade@0.3.0(imy6orjkbvu6zl5y3h5o7cj2ii)': dependencies: '@univerjs/core': 0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1) '@univerjs/data-validation': 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-numfmt@0.3.0)(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/docs': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) - '@univerjs/docs-ui': 0.3.0(mlkoehwl22dn7dps2b2ws3yr64) + '@univerjs/docs-ui': 0.3.0(a3jyoob4q43zypt3aqzliehg5u) '@univerjs/engine-formula': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/engine-render': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/network': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/sheets': 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-numfmt@0.3.0)(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/sheets-conditional-formatting': 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/sheets@0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-numfmt@0.3.0)(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(dayjs@1.11.10)(rxjs@7.8.1) - '@univerjs/sheets-crosshair-highlight': 0.3.0(osuaysnuk5an2534lddybdvy6q) - '@univerjs/sheets-data-validation': 0.3.0(lcc6z77ydbyglikggl6hma4mzu) - '@univerjs/sheets-drawing-ui': 0.3.0(uxkjav7eb6vhdlqzjr7jv2iw44) + '@univerjs/sheets-crosshair-highlight': 0.3.0(rrme3hyewldaudk4iubtggnhmm) + '@univerjs/sheets-data-validation': 0.3.0(viupcfj3lp6rcxf3nnaburzcbq) + '@univerjs/sheets-drawing-ui': 0.3.0(dt4q5z4n2ms4cgtmtyf7ftbsmm) '@univerjs/sheets-filter': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/sheets@0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-numfmt@0.3.0)(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) - '@univerjs/sheets-filter-ui': 0.3.0(r4e37yrxtp4wloo3z3d6lf7tle) - '@univerjs/sheets-formula': 0.3.0(4pw4zmvgvh6humise77ns5t3we) + '@univerjs/sheets-filter-ui': 0.3.0(5at64b556fjdcnififw37monde) + '@univerjs/sheets-formula': 0.3.0(qx6hfmzvsn7yy2npxnlfsjh5ia) '@univerjs/sheets-hyper-link': 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/sheets@0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-numfmt@0.3.0)(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) - '@univerjs/sheets-hyper-link-ui': 0.3.0(q3rejqbdfd35b4pncw6ztjxbpu) - '@univerjs/sheets-numfmt': 0.3.0(yrvbiufgjgmrvr4twxnxho5ula) - '@univerjs/sheets-thread-comment': 0.3.0(iavwxnzbabls3jsuy3pbwuxnm4) - '@univerjs/sheets-ui': 0.3.0(v4qlldkypixlkfvwhtaclquoh4) + '@univerjs/sheets-hyper-link-ui': 0.3.0(k6otisje6ppbra4if5wh2f5dxe) + '@univerjs/sheets-numfmt': 0.3.0(32tk52lx3rf7tfzmmeqtcyvmja) + '@univerjs/sheets-thread-comment': 0.3.0(l7azxsyonq2txuh4uazwrnruxe) + '@univerjs/sheets-ui': 0.3.0(nzhnn66wgwj324iz25zuis64ie) '@univerjs/thread-comment': 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) - '@univerjs/thread-comment-ui': 0.3.0(5ec3rtq5dnbsnnt6wtbclvskvq) - '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) + '@univerjs/thread-comment-ui': 0.3.0(jf6qlqpp5ofimz2w3hemekjcqa) + '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) rxjs: 7.8.1 transitivePeerDependencies: - '@grpc/grpc-js' @@ -24640,38 +24713,38 @@ snapshots: transitivePeerDependencies: - '@grpc/grpc-js' - '@univerjs/sheets-crosshair-highlight@0.3.0(osuaysnuk5an2534lddybdvy6q)': + '@univerjs/sheets-crosshair-highlight@0.3.0(rrme3hyewldaudk4iubtggnhmm)': dependencies: '@univerjs/core': 0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1) '@univerjs/engine-render': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/icons': 0.1.87(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/sheets': 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-numfmt@0.3.0)(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) - '@univerjs/sheets-ui': 0.3.0(v4qlldkypixlkfvwhtaclquoh4) - '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) - clsx: 2.1.0 + '@univerjs/sheets-ui': 0.3.0(nzhnn66wgwj324iz25zuis64ie) + '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) + clsx: 2.1.1 react: 18.3.1 rxjs: 7.8.1 transitivePeerDependencies: - react-dom - '@univerjs/sheets-data-validation@0.3.0(lcc6z77ydbyglikggl6hma4mzu)': + '@univerjs/sheets-data-validation@0.3.0(viupcfj3lp6rcxf3nnaburzcbq)': dependencies: '@flatten-js/interval-tree': 1.1.3 '@univerjs/core': 0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1) '@univerjs/data-validation': 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-numfmt@0.3.0)(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) - '@univerjs/design': 0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@univerjs/design': 0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/docs': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) - '@univerjs/docs-ui': 0.3.0(mlkoehwl22dn7dps2b2ws3yr64) + '@univerjs/docs-ui': 0.3.0(a3jyoob4q43zypt3aqzliehg5u) '@univerjs/engine-formula': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/engine-render': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/icons': 0.1.87(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/protocol': 0.1.39-alpha.15(@grpc/grpc-js@1.12.4)(rxjs@7.8.1) '@univerjs/sheets': 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-numfmt@0.3.0)(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) - '@univerjs/sheets-formula': 0.3.0(4pw4zmvgvh6humise77ns5t3we) - '@univerjs/sheets-numfmt': 0.3.0(yrvbiufgjgmrvr4twxnxho5ula) - '@univerjs/sheets-ui': 0.3.0(v4qlldkypixlkfvwhtaclquoh4) - '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) - clsx: 2.1.0 + '@univerjs/sheets-formula': 0.3.0(qx6hfmzvsn7yy2npxnlfsjh5ia) + '@univerjs/sheets-numfmt': 0.3.0(32tk52lx3rf7tfzmmeqtcyvmja) + '@univerjs/sheets-ui': 0.3.0(nzhnn66wgwj324iz25zuis64ie) + '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) + clsx: 2.1.1 dayjs: 1.11.10 react: 18.3.1 rxjs: 7.8.1 @@ -24679,19 +24752,19 @@ snapshots: - '@grpc/grpc-js' - react-dom - '@univerjs/sheets-drawing-ui@0.3.0(uxkjav7eb6vhdlqzjr7jv2iw44)': + '@univerjs/sheets-drawing-ui@0.3.0(dt4q5z4n2ms4cgtmtyf7ftbsmm)': dependencies: '@univerjs/core': 0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1) - '@univerjs/design': 0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@univerjs/design': 0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/drawing': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1) - '@univerjs/drawing-ui': 0.3.0(enqntv6hnjjl7d3w4jlt2ndy3i) + '@univerjs/drawing-ui': 0.3.0(eomp5knungglzd5skzbvl7ekpe) '@univerjs/engine-render': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/icons': 0.1.87(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/sheets': 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-numfmt@0.3.0)(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/sheets-drawing': 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/drawing@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-numfmt@0.3.0)(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) - '@univerjs/sheets-ui': 0.3.0(v4qlldkypixlkfvwhtaclquoh4) - '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) - clsx: 2.1.0 + '@univerjs/sheets-ui': 0.3.0(nzhnn66wgwj324iz25zuis64ie) + '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) + clsx: 2.1.1 react: 18.3.1 rxjs: 7.8.1 transitivePeerDependencies: @@ -24709,17 +24782,17 @@ snapshots: - '@univerjs/rpc' - rxjs - '@univerjs/sheets-filter-ui@0.3.0(r4e37yrxtp4wloo3z3d6lf7tle)': + '@univerjs/sheets-filter-ui@0.3.0(5at64b556fjdcnififw37monde)': dependencies: '@univerjs/core': 0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1) - '@univerjs/design': 0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@univerjs/design': 0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/engine-render': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/icons': 0.1.87(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/rpc': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/sheets': 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-numfmt@0.3.0)(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/sheets-filter': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/sheets@0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-numfmt@0.3.0)(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) - '@univerjs/sheets-ui': 0.3.0(v4qlldkypixlkfvwhtaclquoh4) - '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) + '@univerjs/sheets-ui': 0.3.0(nzhnn66wgwj324iz25zuis64ie) + '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) clsx: 2.1.1 rc-virtual-list: 3.16.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 @@ -24732,41 +24805,41 @@ snapshots: '@univerjs/sheets': 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-numfmt@0.3.0)(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) rxjs: 7.8.1 - '@univerjs/sheets-formula@0.3.0(4pw4zmvgvh6humise77ns5t3we)': + '@univerjs/sheets-formula@0.3.0(qx6hfmzvsn7yy2npxnlfsjh5ia)': dependencies: '@univerjs/core': 0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1) - '@univerjs/design': 0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@univerjs/design': 0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/docs': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) - '@univerjs/docs-ui': 0.3.0(mlkoehwl22dn7dps2b2ws3yr64) + '@univerjs/docs-ui': 0.3.0(a3jyoob4q43zypt3aqzliehg5u) '@univerjs/engine-formula': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/engine-render': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/icons': 0.1.87(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/rpc': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/sheets': 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-numfmt@0.3.0)(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) - '@univerjs/sheets-numfmt': 0.3.0(yrvbiufgjgmrvr4twxnxho5ula) - '@univerjs/sheets-ui': 0.3.0(v4qlldkypixlkfvwhtaclquoh4) - '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) + '@univerjs/sheets-numfmt': 0.3.0(32tk52lx3rf7tfzmmeqtcyvmja) + '@univerjs/sheets-ui': 0.3.0(nzhnn66wgwj324iz25zuis64ie) + '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) react: 18.3.1 rxjs: 7.8.1 transitivePeerDependencies: - '@univerjs/engine-numfmt' - react-dom - '@univerjs/sheets-hyper-link-ui@0.3.0(q3rejqbdfd35b4pncw6ztjxbpu)': + '@univerjs/sheets-hyper-link-ui@0.3.0(k6otisje6ppbra4if5wh2f5dxe)': dependencies: '@univerjs/core': 0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1) - '@univerjs/design': 0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@univerjs/design': 0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/docs': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/docs-hyper-link': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@univerjs/docs-ui': 0.3.0(mlkoehwl22dn7dps2b2ws3yr64) + '@univerjs/docs-ui': 0.3.0(a3jyoob4q43zypt3aqzliehg5u) '@univerjs/engine-formula': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/engine-render': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/icons': 0.1.87(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/sheets': 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-numfmt@0.3.0)(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/sheets-hyper-link': 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/sheets@0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-numfmt@0.3.0)(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) - '@univerjs/sheets-ui': 0.3.0(v4qlldkypixlkfvwhtaclquoh4) - '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) - clsx: 2.1.0 + '@univerjs/sheets-ui': 0.3.0(nzhnn66wgwj324iz25zuis64ie) + '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) + clsx: 2.1.1 react: 18.3.1 rxjs: 7.8.1 transitivePeerDependencies: @@ -24783,31 +24856,31 @@ snapshots: transitivePeerDependencies: - '@grpc/grpc-js' - '@univerjs/sheets-numfmt@0.3.0(yrvbiufgjgmrvr4twxnxho5ula)': + '@univerjs/sheets-numfmt@0.3.0(32tk52lx3rf7tfzmmeqtcyvmja)': dependencies: '@univerjs/core': 0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1) - '@univerjs/design': 0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@univerjs/design': 0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/engine-formula': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/engine-numfmt': 0.3.0 '@univerjs/engine-render': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/icons': 0.1.87(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/sheets': 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-numfmt@0.3.0)(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) - '@univerjs/sheets-ui': 0.3.0(v4qlldkypixlkfvwhtaclquoh4) - '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) + '@univerjs/sheets-ui': 0.3.0(nzhnn66wgwj324iz25zuis64ie) + '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) react: 18.3.1 rxjs: 7.8.1 transitivePeerDependencies: - '@univerjs/rpc' - react-dom - '@univerjs/sheets-thread-comment-base@0.3.0(r7hkyf7pfqc4ixh2slqfkdti24)': + '@univerjs/sheets-thread-comment-base@0.3.0(hzs5fffcgcszqx56qchizkrhby)': dependencies: '@univerjs/core': 0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1) - '@univerjs/design': 0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@univerjs/design': 0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/engine-formula': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/sheets': 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-numfmt@0.3.0)(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/thread-comment': 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) - '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) + '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) rxjs: 7.8.1 transitivePeerDependencies: - '@univerjs/engine-render' @@ -24821,7 +24894,7 @@ snapshots: - react-dom - typescript - '@univerjs/sheets-thread-comment@0.3.0(iavwxnzbabls3jsuy3pbwuxnm4)': + '@univerjs/sheets-thread-comment@0.3.0(l7azxsyonq2txuh4uazwrnruxe)': dependencies: '@univerjs/core': 0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1) '@univerjs/design': 0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -24829,11 +24902,11 @@ snapshots: '@univerjs/engine-render': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/icons': 0.1.87(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/sheets': 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-numfmt@0.3.0)(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) - '@univerjs/sheets-thread-comment-base': 0.3.0(r7hkyf7pfqc4ixh2slqfkdti24) - '@univerjs/sheets-ui': 0.3.0(v4qlldkypixlkfvwhtaclquoh4) + '@univerjs/sheets-thread-comment-base': 0.3.0(hzs5fffcgcszqx56qchizkrhby) + '@univerjs/sheets-ui': 0.3.0(nzhnn66wgwj324iz25zuis64ie) '@univerjs/thread-comment': 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) - '@univerjs/thread-comment-ui': 0.3.0(5ec3rtq5dnbsnnt6wtbclvskvq) - '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) + '@univerjs/thread-comment-ui': 0.3.0(jf6qlqpp5ofimz2w3hemekjcqa) + '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) clsx: 2.1.1 react: 18.3.1 rxjs: 7.8.1 @@ -24844,20 +24917,20 @@ snapshots: - moment - react-dom - '@univerjs/sheets-ui@0.3.0(v4qlldkypixlkfvwhtaclquoh4)': + '@univerjs/sheets-ui@0.3.0(nzhnn66wgwj324iz25zuis64ie)': dependencies: '@univerjs/core': 0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1) - '@univerjs/design': 0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@univerjs/design': 0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/docs': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) - '@univerjs/docs-ui': 0.3.0(mlkoehwl22dn7dps2b2ws3yr64) + '@univerjs/docs-ui': 0.3.0(a3jyoob4q43zypt3aqzliehg5u) '@univerjs/engine-formula': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/engine-render': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/icons': 0.1.87(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/protocol': 0.1.39-alpha.15(@grpc/grpc-js@1.12.4)(rxjs@7.8.1) '@univerjs/sheets': 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-formula@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/engine-numfmt@0.3.0)(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/telemetry': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1)) - '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) - clsx: 2.1.0 + '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) + clsx: 2.1.1 react: 18.3.1 rxjs: 7.8.1 transitivePeerDependencies: @@ -24880,18 +24953,18 @@ snapshots: dependencies: '@univerjs/core': 0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1) - '@univerjs/thread-comment-ui@0.3.0(5ec3rtq5dnbsnnt6wtbclvskvq)': + '@univerjs/thread-comment-ui@0.3.0(jf6qlqpp5ofimz2w3hemekjcqa)': dependencies: '@univerjs/core': 0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1) - '@univerjs/design': 0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@univerjs/design': 0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/docs': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) - '@univerjs/docs-ui': 0.3.0(mlkoehwl22dn7dps2b2ws3yr64) + '@univerjs/docs-ui': 0.3.0(a3jyoob4q43zypt3aqzliehg5u) '@univerjs/engine-render': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/icons': 0.1.87(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/protocol': 0.1.39-alpha.15(@grpc/grpc-js@1.12.4)(rxjs@7.8.1) '@univerjs/thread-comment': 0.3.0(@grpc/grpc-js@1.12.4)(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) - '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) - clsx: 2.1.0 + '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) + clsx: 2.1.1 dayjs: 1.11.10 react: 18.3.1 rxjs: 7.8.1 @@ -24907,15 +24980,15 @@ snapshots: transitivePeerDependencies: - '@grpc/grpc-js' - '@univerjs/ui@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3)': + '@univerjs/ui@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3)': dependencies: '@univerjs/core': 0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1) - '@univerjs/design': 0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@univerjs/design': 0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@univerjs/engine-formula': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/engine-render': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1) '@univerjs/icons': 0.1.87(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.0)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) - clsx: 2.1.0 + '@univerjs/ui': 0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(@univerjs/design@0.3.0(clsx@2.1.1)(date-fns@2.30.0)(dayjs@1.11.10)(luxon@3.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@univerjs/engine-render@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(@univerjs/rpc@0.3.0(@univerjs/core@0.3.0(@grpc/grpc-js@1.12.4)(react@18.3.1)(rxjs@7.8.1))(rxjs@7.8.1))(clsx@2.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rxjs@7.8.1)(typescript@5.4.3) + clsx: 2.1.1 localforage: 1.10.0 rc-notification: 5.6.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) rc-util: 5.44.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -25684,13 +25757,13 @@ snapshots: axios@0.21.4: dependencies: - follow-redirects: 1.15.9(debug@4.4.0) + follow-redirects: 1.15.9 transitivePeerDependencies: - debug axios@1.7.7: dependencies: - follow-redirects: 1.15.9(debug@4.4.0) + follow-redirects: 1.15.9 form-data: 4.0.0 proxy-from-env: 1.1.0 transitivePeerDependencies: @@ -27655,7 +27728,7 @@ snapshots: '@typescript-eslint/parser': 7.3.1(eslint@8.57.0)(typescript@5.4.3) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0) eslint-plugin-react: 7.34.1(eslint@8.57.0) @@ -27678,24 +27751,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0): - dependencies: - debug: 4.4.0 - enhanced-resolve: 5.17.1 - eslint: 8.57.0 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) - fast-glob: 3.3.2 - get-tsconfig: 4.7.3 - is-core-module: 2.16.0 - is-glob: 4.0.3 - transitivePeerDependencies: - - '@typescript-eslint/parser' - - eslint-import-resolver-node - - eslint-import-resolver-webpack - - supports-color - - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-plugin-import@2.29.1)(eslint@8.57.0): + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0): dependencies: debug: 4.4.0 enhanced-resolve: 5.17.1 @@ -27733,17 +27789,6 @@ snapshots: - bluebird - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): - dependencies: - debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 7.3.1(eslint@8.57.0)(typescript@5.4.3) - eslint: 8.57.0 - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0) - transitivePeerDependencies: - - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): dependencies: debug: 3.2.7 @@ -27751,7 +27796,7 @@ snapshots: '@typescript-eslint/parser': 7.3.1(eslint@8.57.0)(typescript@5.4.3) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) transitivePeerDependencies: - supports-color @@ -28057,6 +28102,8 @@ snapshots: eventsource-parser@1.1.2: {} + eventsource-parser@3.0.0: {} + evp_bytestokey@1.0.3: dependencies: md5.js: 1.3.5 @@ -28443,6 +28490,8 @@ snapshots: fn.name@1.1.0: {} + follow-redirects@1.15.9: {} + follow-redirects@1.15.9(debug@4.4.0): optionalDependencies: debug: 4.4.0 @@ -31544,6 +31593,8 @@ snapshots: nanoid@3.3.7: {} + nanoid@3.3.8: {} + nanoid@5.0.7: {} nanomatch@1.2.13: @@ -36056,9 +36107,9 @@ snapshots: - supports-color - typescript - vite-plugin-svgr@4.2.0(rollup@2.79.2)(typescript@5.4.3)(vite@5.4.11(@types/node@20.9.0)(terser@5.37.0)): + vite-plugin-svgr@4.2.0(rollup@4.28.1)(typescript@5.4.3)(vite@5.4.11(@types/node@20.9.0)(terser@5.37.0)): dependencies: - '@rollup/pluginutils': 5.1.4(rollup@2.79.2) + '@rollup/pluginutils': 5.1.4(rollup@4.28.1) '@svgr/core': 8.1.0(typescript@5.4.3) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.4.3)) vite: 5.4.11(@types/node@20.9.0)(terser@5.37.0) From 4d839b84b3c7da65be568599bb27c77feda0ad1f Mon Sep 17 00:00:00 2001 From: SkyHuang Date: Fri, 27 Dec 2024 12:28:24 +0800 Subject: [PATCH 11/20] chore: update i18n for ai config --- .../common-i18n/src/locales/en/common.json | 5 ++- packages/common-i18n/src/locales/en/sdk.json | 3 +- .../common-i18n/src/locales/fr/common.json | 36 ++++++++++++++++++- packages/common-i18n/src/locales/fr/sdk.json | 4 ++- .../common-i18n/src/locales/ja/common.json | 36 ++++++++++++++++++- packages/common-i18n/src/locales/ja/sdk.json | 4 ++- .../common-i18n/src/locales/ru/common.json | 36 ++++++++++++++++++- packages/common-i18n/src/locales/ru/sdk.json | 4 ++- .../common-i18n/src/locales/zh/common.json | 36 ++++++++++++++++++- packages/common-i18n/src/locales/zh/sdk.json | 4 ++- 10 files changed, 158 insertions(+), 10 deletions(-) diff --git a/packages/common-i18n/src/locales/en/common.json b/packages/common-i18n/src/locales/en/common.json index 6472bd8109..5154d552d3 100644 --- a/packages/common-i18n/src/locales/en/common.json +++ b/packages/common-i18n/src/locales/en/common.json @@ -269,7 +269,10 @@ "translationModelDescription": "The translation model to use", "codingModel": "Coding model", "codingModelDescription": "The coding model to use", - "configUpdated": "AI config updated" + "configUpdated": "AI config updated", + "noModelFound": "No model found.", + "searchModel": "Search model...", + "selectModel": "Select model..." } } }, diff --git a/packages/common-i18n/src/locales/en/sdk.json b/packages/common-i18n/src/locales/en/sdk.json index f4be37eba6..34544fe2ff 100644 --- a/packages/common-i18n/src/locales/en/sdk.json +++ b/packages/common-i18n/src/locales/en/sdk.json @@ -55,7 +55,8 @@ "guideExample": "Example", "helperExample": "Example: ", "fieldValue": "Returns the value to the cells of the {{fieldName}} field.", - "placeholder": "Enter an expression or press // to generate with AI" + "placeholder": "Enter an expression", + "placeholderForAI": "Enter an expression or press // to generate with AI" }, "link": { "placeholder": "Select records to link", diff --git a/packages/common-i18n/src/locales/fr/common.json b/packages/common-i18n/src/locales/fr/common.json index 4dee21a8a9..03d8128303 100644 --- a/packages/common-i18n/src/locales/fr/common.json +++ b/packages/common-i18n/src/locales/fr/common.json @@ -224,7 +224,41 @@ "allowSpaceInvitation": "Autoriser l'envoi d'invitations à des espaces", "allowSpaceInvitationDescription": "Désactiver cette option empêchera les utilisateurs autres que les administrateurs d'inviter d'autres personnes à rejoindre des espaces. Activer cette option permettra aux utilisateurs invités directement de créer un compte même si l'inscription de nouveaux comptes est désactivée.", "allowSpaceCreation": "Autoriser tout le monde à créer de nouveaux espaces", - "allowSpaceCreationDescription": "Désactiver cette option empêchera les utilisateurs autres que les administrateurs de créer de nouveaux espaces." + "allowSpaceCreationDescription": "Désactiver cette option empêchera les utilisateurs autres que les administrateurs de créer de nouveaux espaces.", + "generalSettings": "Paramètres généraux", + "aiSettings": "Paramètres AI", + "ai": { + "name": "Nom", + "nameDescription": "Le nom du fournisseur LLM", + "enable": "Activer AI", + "enableDescription": "Activer AI pour l'instance actuelle, tous les utilisateurs pourront utiliser les fonctionnalités AI", + "updateLLMProvider": "Mettre à jour le fournisseur LLM", + "addProvider": "Ajouter un fournisseur LLM", + "addProviderDescription": "Ajouter un nouveau fournisseur LLM à la liste", + "providerType": "Type de fournisseur", + "baseUrl": "URL de base", + "apiKey": "Clé API", + "baseUrlDescription": "L'URL de base du fournisseur LLM", + "apiKeyDescription": "La clé API du fournisseur LLM", + "models": "Modèles", + "modelsDescription": "Les modèles pris en charge par le fournisseur LLM", + "baseUrlRequired": "L'URL de base est requise", + "fetchModelListError": "Impossible de récupérer la liste des modèles", + "provider": "Fournisseur LLM", + "providerDescription": "Le fournisseur LLM à utiliser", + "modelPreferences": "Préférences des modèles", + "modelPreferencesDescription": "Les préférences des modèles pour le fournisseur LLM", + "embeddingModel": "Modèle d'intégration", + "embeddingModelDescription": "Le modèle d'intégration à utiliser", + "translationModel": "Modèle de traduction", + "translationModelDescription": "Le modèle de traduction à utiliser", + "codingModel": "Modèle de codage", + "codingModelDescription": "Le modèle de codage à utiliser", + "configUpdated": "Paramètres AI mis à jour", + "noModelFound": "Aucun modèle trouvé", + "searchModel": "Rechercher un modèle...", + "selectModel": "Sélectionner un modèle..." + } } }, "notification": { diff --git a/packages/common-i18n/src/locales/fr/sdk.json b/packages/common-i18n/src/locales/fr/sdk.json index 02d2fdae76..d1d6497ef1 100644 --- a/packages/common-i18n/src/locales/fr/sdk.json +++ b/packages/common-i18n/src/locales/fr/sdk.json @@ -41,7 +41,9 @@ "guideSyntax": "Syntaxe", "guideExample": "Exemple", "helperExample": "Exemple : ", - "fieldValue": "Renvoie la valeur aux cellules du champ {{fieldName}}." + "fieldValue": "Renvoie la valeur aux cellules du champ {{fieldName}}.", + "placeholder": "Entrez une expression", + "placeholderForAI": "Entrez une expression ou entrez // pour générer avec AI" }, "link": { "placeholder": "Sélectionner des enregistrements à lier", diff --git a/packages/common-i18n/src/locales/ja/common.json b/packages/common-i18n/src/locales/ja/common.json index fd18a5a95c..3b7cb1911e 100644 --- a/packages/common-i18n/src/locales/ja/common.json +++ b/packages/common-i18n/src/locales/ja/common.json @@ -229,7 +229,41 @@ "allowSpaceInvitation": "スペースへの招待の送信を許可する", "allowSpaceInvitationDescription": "このオプションを無効にすると、管理者以外のユーザーは他のユーザーをスペースに招待できなくなります。このオプションを有効にすると、新規アカウント登録が無効になっている場合でも、直接招待されたユーザーはアカウントを作成できるようになります。", "allowSpaceCreation": "誰でも新しいスペースを作成できるようにする", - "allowSpaceCreationDescription": "このオプションを無効にすると、管理者以外のユーザーは新しいスペースを作成できなくなります。" + "allowSpaceCreationDescription": "このオプションを無効にすると、管理者以外のユーザーは新しいスペースを作成できなくなります。", + "generalSettings": "一般設定", + "aiSettings": "AI設定", + "ai": { + "name": "名前", + "nameDescription": "LLMプロバイダーの名前", + "enable": "AIを有効にする", + "enableDescription": "現在のインスタンスでAIを有効にし、すべてのユーザーがAI機能を使用できるようにする", + "updateLLMProvider": "LLMプロバイダーを更新する", + "addProvider": "LLMプロバイダーを追加する", + "addProviderDescription": "LLMプロバイダーのリストに新しいプロバイダーを追加する", + "providerType": "プロバイダータイプ", + "baseUrl": "ベースURL", + "apiKey": "APIキー", + "baseUrlDescription": "LLMプロバイダーのベースURL", + "apiKeyDescription": "LLMプロバイダーのAPIキー", + "models": "モデル", + "modelsDescription": "LLMプロバイダーでサポートされているモデル", + "baseUrlRequired": "ベースURLは必須です", + "fetchModelListError": "モデルリストの取得に失敗しました", + "provider": "LLMプロバイダー", + "providerDescription": "使用するLLMプロバイダー", + "modelPreferences": "モデルの設定", + "modelPreferencesDescription": "LLMプロバイダーのモデルの設定", + "embeddingModel": "埋め込みモデル", + "embeddingModelDescription": "使用する埋め込みモデル", + "translationModel": "翻訳モデル", + "translationModelDescription": "使用する翻訳モデル", + "codingModel": "コーディングモデル", + "codingModelDescription": "使用するコーディングモデル", + "configUpdated": "AI設定が更新されました", + "noModelFound": "モデルが見つかりません", + "searchModel": "モデルを検索...", + "selectModel": "モデルを選択..." + } } }, "notification": { diff --git a/packages/common-i18n/src/locales/ja/sdk.json b/packages/common-i18n/src/locales/ja/sdk.json index 338b120ea4..372982d1d7 100644 --- a/packages/common-i18n/src/locales/ja/sdk.json +++ b/packages/common-i18n/src/locales/ja/sdk.json @@ -41,7 +41,9 @@ "guideSyntax": "構文", "guideExample": "例", "helperExample": "例: ", - "fieldValue": "{{fieldName}}フィールドのセルに値を返します。" + "fieldValue": "{{fieldName}}フィールドのセルに値を返します。", + "placeholder": "数式を入力", + "placeholderForAI": "数式を入力するか、//を入力してAIで生成" }, "link": { "placeholder": "リンクするレコードを選択", diff --git a/packages/common-i18n/src/locales/ru/common.json b/packages/common-i18n/src/locales/ru/common.json index 0a15d6c10e..24620ce2bb 100644 --- a/packages/common-i18n/src/locales/ru/common.json +++ b/packages/common-i18n/src/locales/ru/common.json @@ -231,7 +231,41 @@ "allowSpaceInvitation": "Разрешить приглашения в пространство", "allowSpaceInvitationDescription": "Отключение этой опции запретит пользователям, кроме администраторов, приглашать других в пространство. Включение позволит приглашённым создавать аккаунт, даже если регистрация новых аккаунтов отключена.", "allowSpaceCreation": "Разрешить всем создавать новые пространства", - "allowSpaceCreationDescription": "Отключение этой опции запретит пользователям, кроме администраторов, создавать новые пространства." + "allowSpaceCreationDescription": "Отключение этой опции запретит пользователям, кроме администраторов, создавать новые пространства.", + "generalSettings": "Общие настройки", + "aiSettings": "AI настройки", + "ai": { + "name": "Имя", + "nameDescription": "Имя LLM провайдера", + "enable": "Включить AI", + "enableDescription": "Включить AI для текущего экземпляра, все пользователи смогут использовать функции AI", + "updateLLMProvider": "Обновить LLM провайдера", + "addProvider": "Добавить LLM провайдера", + "addProviderDescription": "Добавить новый LLM провайдера в список", + "providerType": "Тип провайдера", + "baseUrl": "Базовый URL", + "apiKey": "API ключ", + "baseUrlDescription": "Базовый URL LLM провайдера", + "apiKeyDescription": "API ключ LLM провайдера", + "models": "Модели", + "modelsDescription": "Модели, поддерживаемые LLM провайдером", + "baseUrlRequired": "Базовый URL обязателен", + "fetchModelListError": "Не удалось получить список моделей", + "provider": "LLM провайдер", + "providerDescription": "LLM провайдер для использования", + "modelPreferences": "Модель предпочтения", + "modelPreferencesDescription": "Предпочтения модели для LLM провайдера", + "embeddingModel": "Модель встраивания", + "embeddingModelDescription": "Модель встраивания для использования", + "translationModel": "Модель перевода", + "translationModelDescription": "Модель перевода для использования", + "codingModel": "Модель кодирования", + "codingModelDescription": "Модель кодирования для использования", + "configUpdated": "AI настройки обновлены", + "noModelFound": "Модель не найдена", + "searchModel": "Поиск модели...", + "selectModel": "Выбрать модель..." + } } }, "notification": { diff --git a/packages/common-i18n/src/locales/ru/sdk.json b/packages/common-i18n/src/locales/ru/sdk.json index 52bfd911de..72df8fff9a 100644 --- a/packages/common-i18n/src/locales/ru/sdk.json +++ b/packages/common-i18n/src/locales/ru/sdk.json @@ -53,7 +53,9 @@ "guideSyntax": "Синтаксис", "guideExample": "Пример", "helperExample": "Пример: ", - "fieldValue": "Возвращает значение для поля {{fieldName}}." + "fieldValue": "Возвращает значение для поля {{fieldName}}.", + "placeholder": "Введите формулу", + "placeholderForAI": "Введите формулу или введите // для генерации с помощью AI" }, "link": { "placeholder": "Выберите записи для ссылки", diff --git a/packages/common-i18n/src/locales/zh/common.json b/packages/common-i18n/src/locales/zh/common.json index 7e60b3cdb3..c4901e01ce 100644 --- a/packages/common-i18n/src/locales/zh/common.json +++ b/packages/common-i18n/src/locales/zh/common.json @@ -238,7 +238,41 @@ "allowSpaceInvitation": "允许发送空间邀请", "allowSpaceInvitationDescription": "关闭此选项将禁止除管理员以外的用户邀请他人加入空间。开启此选项后,即使新账户注册已被禁用,仍允许被直接邀请的用户创建账户。", "allowSpaceCreation": "允许所有人创建新的空间", - "allowSpaceCreationDescription": "关闭此选项将禁止除管理员以外的用户创建新的空间。" + "allowSpaceCreationDescription": "关闭此选项将禁止除管理员以外的用户创建新的空间。", + "generalSettings": "通用配置", + "aiSettings": "AI 配置", + "ai": { + "name": "名称", + "nameDescription": "模型提供商的名称", + "enable": "启用 AI", + "enableDescription": "启用当前实例的 AI 功能,所有用户将能够使用 AI 功能", + "updateLLMProvider": "更新模型提供商", + "addProvider": "添加模型提供商", + "addProviderDescription": "添加一个新的模型提供商到列表", + "providerType": "提供商类型", + "baseUrl": "基础 URL", + "apiKey": "API 密钥", + "baseUrlDescription": "模型提供商的基础 URL", + "apiKeyDescription": "模型提供商的 API 密钥", + "models": "模型", + "modelsDescription": "模型提供商支持的模型", + "baseUrlRequired": "基础 URL 是必需的", + "fetchModelListError": "无法获取模型列表", + "provider": "模型提供商", + "providerDescription": "要使用的模型提供商", + "modelPreferences": "模型偏好", + "modelPreferencesDescription": "模型提供商的模型偏好", + "embeddingModel": "嵌入模型", + "embeddingModelDescription": "要使用的嵌入模型", + "translationModel": "翻译模型", + "translationModelDescription": "要使用的翻译模型", + "codingModel": "编码模型", + "codingModelDescription": "要使用的编码模型", + "configUpdated": "AI 配置已更新", + "noModelFound": "未找到模型", + "searchModel": "搜索模型...", + "selectModel": "选择模型..." + } } }, "notification": { diff --git a/packages/common-i18n/src/locales/zh/sdk.json b/packages/common-i18n/src/locales/zh/sdk.json index 28f13ea4cc..77d62fa5e7 100644 --- a/packages/common-i18n/src/locales/zh/sdk.json +++ b/packages/common-i18n/src/locales/zh/sdk.json @@ -68,7 +68,9 @@ "guideSyntax": "语法", "guideExample": "示例", "helperExample": "示例:", - "fieldValue": "返回 {{fieldName}} 字段的值." + "fieldValue": "返回 {{fieldName}} 字段的值.", + "placeholder": "请输入公式", + "placeholderForAI": "请输入公式或输入以 // 开头的注释使用 AI 生成公式" }, "link": { "placeholder": "选择要链接的记录", From b8ce3f4eb2c206eaa1cec1710c55f7c542042cfc Mon Sep 17 00:00:00 2001 From: SkyHuang Date: Fri, 27 Dec 2024 12:28:52 +0800 Subject: [PATCH 12/20] perf: formula editor ai interaction --- .../src/components/editor/formula/Editor.tsx | 12 +++--- .../editor/formula/components/CodeEditor.tsx | 41 ++++++------------- 2 files changed, 20 insertions(+), 33 deletions(-) diff --git a/packages/sdk/src/components/editor/formula/Editor.tsx b/packages/sdk/src/components/editor/formula/Editor.tsx index 17647eae92..b645d853e5 100644 --- a/packages/sdk/src/components/editor/formula/Editor.tsx +++ b/packages/sdk/src/components/editor/formula/Editor.tsx @@ -10,6 +10,7 @@ import { Button, cn } from '@teable/ui-lib'; import { CharStreams } from 'antlr4ts'; import Fuse from 'fuse.js'; import { cloneDeep, keyBy } from 'lodash'; +import { AlertCircle } from 'lucide-react'; import type { FC } from 'react'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from '../../../context/app/i18n'; @@ -35,7 +36,6 @@ import type { } from './interface'; import { SuggestionItemType } from './interface'; import { FormulaNodePathVisitor } from './visitor'; -import { AlertCircle } from 'lucide-react'; interface IFormulaEditorProps { expression?: string; @@ -346,7 +346,7 @@ export const FormulaEditor: FC = (props) => { if (expressionByName.startsWith('//')) { generateAIResponse(getFormulaPrompt(expressionByName.slice(2), fields)); } - }, [expressionByName, fields, loading]); + }, [expressionByName, fields, generateAIResponse, loading]); const codeBg = isLightTheme ? 'bg-slate-100' : 'bg-gray-900'; // only generate formula when the expression starts with // @@ -372,7 +372,7 @@ export const FormulaEditor: FC = (props) => { {error && (
- + {error}
)} @@ -381,14 +381,16 @@ export const FormulaEditor: FC = (props) => {
-
+
{errMsg}
diff --git a/packages/sdk/src/components/editor/formula/components/CodeEditor.tsx b/packages/sdk/src/components/editor/formula/components/CodeEditor.tsx index 7e7db8bd5f..0f079cd8ff 100644 --- a/packages/sdk/src/components/editor/formula/components/CodeEditor.tsx +++ b/packages/sdk/src/components/editor/formula/components/CodeEditor.tsx @@ -1,7 +1,7 @@ import { syntaxHighlighting, defaultHighlightStyle } from '@codemirror/language'; import type { EditorSelection, Extension } from '@codemirror/state'; import { EditorState, StateEffect } from '@codemirror/state'; -import { EditorView, Decoration, WidgetType } from '@codemirror/view'; +import { EditorView, placeholder as placeholderExtension } from '@codemirror/view'; import type { ForwardRefRenderFunction } from 'react'; import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef } from 'react'; import { AUTOCOMPLETE_EXTENSIONS, HISTORY_EXTENSIONS } from '../extensions'; @@ -30,6 +30,7 @@ const CodeEditorBase: ForwardRefRenderFunction } = props; const editorRef = useRef(null); const editorViewRef = useRef(null); + const isUserInput = useRef(false); useImperativeHandle(ref, () => ({ getEditorView: () => editorViewRef.current, @@ -38,6 +39,7 @@ const CodeEditorBase: ForwardRefRenderFunction const allExtensions = useMemo(() => { const updateListener = EditorView.updateListener.of((v) => { if (v.docChanged) { + isUserInput.current = true; const value = v.state.doc.toString(); onChange?.(value); } @@ -50,34 +52,13 @@ const CodeEditorBase: ForwardRefRenderFunction }); const highlight = syntaxHighlighting(defaultHighlightStyle, { fallback: true }); - const placeholderExt = placeholder - ? EditorView.decorations.of((view) => { - const doc = view.state.doc; - return doc.length === 0 - ? Decoration.set([ - Decoration.widget({ - widget: new (class extends WidgetType { - toDOM() { - const span = document.createElement('span'); - span.className = 'cm-placeholder'; - span.textContent = placeholder; - return span; - } - })(), - side: 1, - }).range(0), - ]) - : Decoration.none; - }) - : []; - return [ ...HISTORY_EXTENSIONS, ...AUTOCOMPLETE_EXTENSIONS, highlight, updateListener, EditorView.lineWrapping, - placeholderExt, + placeholderExtension(placeholder ?? ''), ...extensions, ]; }, [extensions, onChange, onSelectionChange, placeholder]); @@ -104,12 +85,16 @@ const CodeEditorBase: ForwardRefRenderFunction }, []); useEffect(() => { - if (editorViewRef.current) { - const transaction = editorViewRef.current.state.update({ - changes: { from: 0, to: editorViewRef.current.state.doc.length, insert: value }, - }); - editorViewRef.current.dispatch(transaction); + if (editorViewRef.current && !isUserInput.current) { + const currentValue = editorViewRef.current.state.doc.toString(); + if (currentValue !== value) { + const transaction = editorViewRef.current.state.update({ + changes: { from: 0, to: editorViewRef.current.state.doc.length, insert: value }, + }); + editorViewRef.current.dispatch(transaction); + } } + isUserInput.current = false; }, [value]); useEffect(() => { From 7b5aa2fdcf55bb921b5c34ced8369d89f6fa4bf3 Mon Sep 17 00:00:00 2001 From: SkyHuang Date: Fri, 27 Dec 2024 18:21:07 +0800 Subject: [PATCH 13/20] chore: update icons --- packages/icons/src/components/Anthropic.tsx | 26 +++++++++++++++ packages/icons/src/components/Azure.tsx | 18 ++++++++++ packages/icons/src/components/Cohere.tsx | 33 +++++++++++++++++++ packages/icons/src/components/FileJson.tsx | 28 ++++++++++++++++ packages/icons/src/components/HttpRequest.tsx | 2 +- packages/icons/src/components/License.tsx | 2 +- packages/icons/src/components/Mistral.tsx | 27 +++++++++++++++ packages/icons/src/components/Openai.tsx | 26 +++++++++++++++ packages/icons/src/index.ts | 6 ++++ 9 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 packages/icons/src/components/Anthropic.tsx create mode 100644 packages/icons/src/components/Azure.tsx create mode 100644 packages/icons/src/components/Cohere.tsx create mode 100644 packages/icons/src/components/FileJson.tsx create mode 100644 packages/icons/src/components/Mistral.tsx create mode 100644 packages/icons/src/components/Openai.tsx diff --git a/packages/icons/src/components/Anthropic.tsx b/packages/icons/src/components/Anthropic.tsx new file mode 100644 index 0000000000..776e449fa0 --- /dev/null +++ b/packages/icons/src/components/Anthropic.tsx @@ -0,0 +1,26 @@ +import * as React from 'react'; +import type { SVGProps } from 'react'; +const Anthropic = (props: SVGProps) => ( + + + + + + + + + + + +); +export default Anthropic; diff --git a/packages/icons/src/components/Azure.tsx b/packages/icons/src/components/Azure.tsx new file mode 100644 index 0000000000..6cc42261d1 --- /dev/null +++ b/packages/icons/src/components/Azure.tsx @@ -0,0 +1,18 @@ +import * as React from 'react'; +import type { SVGProps } from 'react'; +const Azure = (props: SVGProps) => ( + + + +); +export default Azure; diff --git a/packages/icons/src/components/Cohere.tsx b/packages/icons/src/components/Cohere.tsx new file mode 100644 index 0000000000..44534665f2 --- /dev/null +++ b/packages/icons/src/components/Cohere.tsx @@ -0,0 +1,33 @@ +import * as React from 'react'; +import type { SVGProps } from 'react'; +const Cohere = (props: SVGProps) => ( + + + + + + + + + + + + +); +export default Cohere; diff --git a/packages/icons/src/components/FileJson.tsx b/packages/icons/src/components/FileJson.tsx new file mode 100644 index 0000000000..812efb4400 --- /dev/null +++ b/packages/icons/src/components/FileJson.tsx @@ -0,0 +1,28 @@ +import * as React from 'react'; +import type { SVGProps } from 'react'; +const FileJson = (props: SVGProps) => ( + + + + +); +export default FileJson; diff --git a/packages/icons/src/components/HttpRequest.tsx b/packages/icons/src/components/HttpRequest.tsx index 89b9f56f64..b1c2edb1ad 100644 --- a/packages/icons/src/components/HttpRequest.tsx +++ b/packages/icons/src/components/HttpRequest.tsx @@ -16,7 +16,7 @@ const HttpRequest = (props: SVGProps) => ( /> ); diff --git a/packages/icons/src/components/License.tsx b/packages/icons/src/components/License.tsx index fbe75de29b..759892f2dd 100644 --- a/packages/icons/src/components/License.tsx +++ b/packages/icons/src/components/License.tsx @@ -15,7 +15,7 @@ const License = (props: SVGProps) => ( /> ); diff --git a/packages/icons/src/components/Mistral.tsx b/packages/icons/src/components/Mistral.tsx new file mode 100644 index 0000000000..f750f28eb4 --- /dev/null +++ b/packages/icons/src/components/Mistral.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import type { SVGProps } from 'react'; +const Mistral = (props: SVGProps) => ( + + + + + + + + + + + + +); +export default Mistral; diff --git a/packages/icons/src/components/Openai.tsx b/packages/icons/src/components/Openai.tsx new file mode 100644 index 0000000000..d462731cd7 --- /dev/null +++ b/packages/icons/src/components/Openai.tsx @@ -0,0 +1,26 @@ +import * as React from 'react'; +import type { SVGProps } from 'react'; +const Openai = (props: SVGProps) => ( + + + + + + + + + + + +); +export default Openai; diff --git a/packages/icons/src/index.ts b/packages/icons/src/index.ts index 4ea8cbfa34..a4ecf88f68 100644 --- a/packages/icons/src/index.ts +++ b/packages/icons/src/index.ts @@ -2,6 +2,7 @@ export { default as A } from './components/A'; export { default as Admin } from './components/Admin'; export { default as AlertCircle } from './components/AlertCircle'; export { default as AlertTriangle } from './components/AlertTriangle'; +export { default as Anthropic } from './components/Anthropic'; export { default as Apple } from './components/Apple'; export { default as Array } from './components/Array'; export { default as ArrowDown } from './components/ArrowDown'; @@ -10,6 +11,7 @@ export { default as ArrowRight } from './components/ArrowRight'; export { default as ArrowUp } from './components/ArrowUp'; export { default as ArrowUpDown } from './components/ArrowUpDown'; export { default as ArrowUpRight } from './components/ArrowUpRight'; +export { default as Azure } from './components/Azure'; export { default as BarChart2 } from './components/BarChart2'; export { default as Bell } from './components/Bell'; export { default as Boolean } from './components/Boolean'; @@ -31,6 +33,7 @@ export { default as ClipboardList } from './components/ClipboardList'; export { default as Clock4 } from './components/Clock4'; export { default as Code } from './components/Code'; export { default as Code2 } from './components/Code2'; +export { default as Cohere } from './components/Cohere'; export { default as Component } from './components/Component'; export { default as Condition } from './components/Condition'; export { default as Copy } from './components/Copy'; @@ -52,6 +55,7 @@ export { default as FileDocument } from './components/FileDocument'; export { default as FileExcel } from './components/FileExcel'; export { default as FileFont } from './components/FileFont'; export { default as FileImage } from './components/FileImage'; +export { default as FileJson } from './components/FileJson'; export { default as FilePack } from './components/FilePack'; export { default as FilePdf } from './components/FilePdf'; export { default as FilePresentation } from './components/FilePresentation'; @@ -98,10 +102,12 @@ export { default as Maximize2 } from './components/Maximize2'; export { default as Menu } from './components/Menu'; export { default as MessageSquare } from './components/MessageSquare'; export { default as Minimize2 } from './components/Minimize2'; +export { default as Mistral } from './components/Mistral'; export { default as Moon } from './components/Moon'; export { default as MoreHorizontal } from './components/MoreHorizontal'; export { default as Network } from './components/Network'; export { default as Object } from './components/Object'; +export { default as Openai } from './components/Openai'; export { default as PackageCheck } from './components/PackageCheck'; export { default as PaintBucket } from './components/PaintBucket'; export { default as Pencil } from './components/Pencil'; From 600f72bb3028b6fd0550929253cd526ec39870ab Mon Sep 17 00:00:00 2001 From: SkyHuang Date: Fri, 27 Dec 2024 18:43:05 +0800 Subject: [PATCH 14/20] perf: formula ai prompt --- packages/sdk/src/components/editor/formula/Editor.tsx | 8 ++++---- .../sdk/src/components/editor/formula/extensions/ai.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/sdk/src/components/editor/formula/Editor.tsx b/packages/sdk/src/components/editor/formula/Editor.tsx index b645d853e5..87c3a0618d 100644 --- a/packages/sdk/src/components/editor/formula/Editor.tsx +++ b/packages/sdk/src/components/editor/formula/Editor.tsx @@ -355,18 +355,18 @@ export const FormulaEditor: FC = (props) => { return (
-
+

{t('editor.formula.title')}

{enableAI && ( -
+
diff --git a/packages/sdk/src/components/editor/formula/extensions/ai.ts b/packages/sdk/src/components/editor/formula/extensions/ai.ts index 5e9f27e8e3..c92019eba0 100644 --- a/packages/sdk/src/components/editor/formula/extensions/ai.ts +++ b/packages/sdk/src/components/editor/formula/extensions/ai.ts @@ -6,7 +6,7 @@ export const getFormulaPrompt = (prompt: string, fields: ReturnType tag, please refer to the field information to generate the formula. + 3. the field information of the current table is in the tag in the format of "fieldId: fieldName", please refer to the field information to generate the formula. 4. the user's description is in the tag. ${context} From b8b46a2c2de944b5a9767614a6277fa2dbcdb325 Mon Sep 17 00:00:00 2001 From: SkyHuang Date: Fri, 27 Dec 2024 18:43:35 +0800 Subject: [PATCH 15/20] feat: ai config --- .../src/features/ai/ai.service.ts | 31 ++++--- .../setting/components/ai-config/ai-form.tsx | 10 +-- .../components/ai-config/ai-model-select.tsx | 83 ++++++++++++------- .../setting/components/ai-config/constant.ts | 38 ++++++--- .../ai-config/new-llm-provider-form.tsx | 9 +- .../setting/components/ai-config/util.ts | 13 +++ .../common-i18n/src/locales/zh/common.json | 2 +- 7 files changed, 127 insertions(+), 59 deletions(-) create mode 100644 apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/util.ts diff --git a/apps/nestjs-backend/src/features/ai/ai.service.ts b/apps/nestjs-backend/src/features/ai/ai.service.ts index 0be84a1cd5..de7fc05bc7 100644 --- a/apps/nestjs-backend/src/features/ai/ai.service.ts +++ b/apps/nestjs-backend/src/features/ai/ai.service.ts @@ -26,21 +26,30 @@ export class AiService { async getModelConfig(task: Task) { const { aiConfig } = await this.settingService.getSetting(); - // aiConfig?.codingModel model@provider + // aiConfig?.codingModel type@model@provider const currentTaskModel = TASK_MODEL_MAP[task]; - const [model, provider] = + const [type, model, provider] = (aiConfig?.[currentTaskModel as keyof typeof aiConfig] as string)?.split('@') || []; const llmProviders = aiConfig?.llmProviders || []; const providerConfig = llmProviders.find( - (p) => p.name.toLowerCase() === provider.toLowerCase() + (p) => + p.name.toLowerCase() === provider.toLowerCase() && + p.type.toLowerCase() === type.toLowerCase() ); if (!providerConfig) { throw new Error('AI provider configuration is not set'); } - return { model, baseUrl: providerConfig.baseUrl, apiKey: providerConfig.apiKey }; + const { baseUrl, apiKey } = providerConfig; + + return { + type, + model, + baseUrl, + apiKey, + }; } async getModelInstance( @@ -48,24 +57,24 @@ export class AiService { ): Promise< ReturnType> > { - const config = await this.getModelConfig(task); + const { type, model, baseUrl, apiKey } = await this.getModelConfig(task); - if (!config.baseUrl || !config.apiKey) { + if (!baseUrl || !apiKey) { throw new Error('AI configuration is not set'); } const provider = Object.entries(this.modelProviders).find(([key]) => - config.model.toLowerCase().includes(key.toLowerCase()) + type.toLowerCase().includes(key.toLowerCase()) )?.[1]; if (!provider) { - throw new Error(`Unsupported AI provider for model: ${config.model}`); + throw new Error(`Unsupported AI provider: ${type}`); } return provider({ - baseURL: config.baseUrl, - apiKey: config.apiKey, - })(config.model); + baseURL: baseUrl, + apiKey, + })(model); } async generate(prompt: string, task: Task = Task.Coding) { diff --git a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-form.tsx b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-form.tsx index 0d0e63cbf0..44de17a810 100644 --- a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-form.tsx +++ b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-form.tsx @@ -24,6 +24,7 @@ import { useForm } from 'react-hook-form'; import { AIModelSelect } from './ai-model-select'; import { LLMProviderManage } from './llm-provider-manage'; +import { generateModelKeyList } from './util'; export function AIConfigForm({ aiConfig, @@ -45,9 +46,8 @@ export function AIConfigForm({ resolver: zodResolver(aiConfigSchema), defaultValues: defaultValues, }); - const models = (form.watch('llmProviders') ?? []).map((provider) => - provider.models.split(',').map((model) => model.trim() + '@' + provider.name) - ); + const llmProviders = form.watch('llmProviders') ?? []; + const models = generateModelKeyList(llmProviders); const { reset } = form; const { t } = useTranslation('common'); @@ -138,7 +138,7 @@ export function AIConfigForm({ field.onChange(value); onSubmit(form.getValues()); }} - options={models.flat()} + options={models} />
@@ -168,7 +168,7 @@ export function AIConfigForm({ field.onChange(value); onSubmit(form.getValues()); }} - options={models.flat()} + options={models} />
diff --git a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-model-select.tsx b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-model-select.tsx index c1a27b1743..e1216078a0 100644 --- a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-model-select.tsx +++ b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-model-select.tsx @@ -16,6 +16,16 @@ import { import { Check, ChevronsUpDown } from 'lucide-react'; import { useTranslation } from 'next-i18next'; import * as React from 'react'; +import { LLM_PROVIDER_ICONS } from './constant'; +import { parseModelKey } from './util'; + +interface IAIModelSelectProps { + value: string; + onValueChange: (value: string) => void; + size?: 'xs' | 'sm' | 'lg' | 'default' | null | undefined; + className?: string; + options?: string[]; +} export function AIModelSelect({ value = '', @@ -23,15 +33,12 @@ export function AIModelSelect({ size = 'default', className, options = [], -}: { - onValueChange: (value: string) => void; - value: string; - size?: 'xs' | 'sm' | 'lg' | 'default' | null | undefined; - className?: string; - options?: string[]; -}) { +}: IAIModelSelectProps) { const [open, setOpen] = React.useState(false); const currentModel = options.find((model) => model.toLowerCase() === value.toLowerCase()); + const { type, name, model } = parseModelKey(currentModel); + const Icon = LLM_PROVIDER_ICONS[type as keyof typeof LLM_PROVIDER_ICONS]; + const { t } = useTranslation('common'); return ( @@ -44,9 +51,19 @@ export function AIModelSelect({ size={size} className={cn('grow justify-between', className)} > -

- {currentModel ?? t('admin.setting.ai.selectModel')} -

+
+ {!currentModel ? ( + t('admin.setting.ai.selectModel') + ) : ( + <> +
{name}
+
+ + {model} +
+ + )} +
@@ -57,24 +74,34 @@ export function AIModelSelect({
- {options.map((model) => ( - { - setValue(model.toLowerCase() === value.toLowerCase() ? '' : model); - setOpen(false); - }} - > - -

{model}

{' '} -
- ))} + {options.map((modelKey) => { + const { type, model, name } = parseModelKey(modelKey); + const Icon = LLM_PROVIDER_ICONS[type as keyof typeof LLM_PROVIDER_ICONS]; + return ( + { + setValue(modelKey.toLowerCase() === value.toLowerCase() ? '' : modelKey); + setOpen(false); + }} + > + +

{name}

+
+ + {model} +
+
+ ); + })}
diff --git a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/constant.ts b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/constant.ts index 8f9a963406..f5e54b4871 100644 --- a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/constant.ts +++ b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/constant.ts @@ -1,40 +1,56 @@ +import { Anthropic, Azure, Cohere, GoogleLogo, Mistral, Openai } from '@teable/icons'; import { LLMProviderType } from '@teable/openapi'; +export const LLM_PROVIDER_ICONS = { + [LLMProviderType.OPENAI]: Openai, + [LLMProviderType.ANTHROPIC]: Anthropic, + [LLMProviderType.GOOGLE]: GoogleLogo, + [LLMProviderType.AZURE]: Azure, + [LLMProviderType.COHERE]: Cohere, + [LLMProviderType.MISTRAL]: Mistral, +}; + export const LLM_PROVIDERS = [ { value: LLMProviderType.OPENAI, label: 'OpenAI', baseUrlPlaceholder: 'https://api.openai.com/v1', - modelsPlaceholder: 'gpt-4, gpt-4o-mini, gpt-3.5-turbo', - }, - { - value: LLMProviderType.AZURE, - label: 'Azure', - baseUrlPlaceholder: 'https://{your-resource-name}.openai.azure.com', - modelsPlaceholder: 'gpt-4, gpt-35-turbo', + modelsPlaceholder: 'gpt-4,gpt-4o-mini,gpt-3.5-turbo', + Icon: LLM_PROVIDER_ICONS[LLMProviderType.OPENAI], }, { value: LLMProviderType.ANTHROPIC, label: 'Anthropic', baseUrlPlaceholder: 'https://api.anthropic.com', - modelsPlaceholder: 'claude-3-opus-20240229, claude-3-5-sonnet-20241022', + modelsPlaceholder: 'claude-3-opus-20240229,claude-3-5-sonnet-20241022', + Icon: LLM_PROVIDER_ICONS[LLMProviderType.ANTHROPIC], }, { value: LLMProviderType.GOOGLE, label: 'Google', baseUrlPlaceholder: 'https://generativelanguage.googleapis.com', - modelsPlaceholder: 'gemini-pro-vision, gemini-1.5-flash-002', + modelsPlaceholder: 'gemini-pro-vision,gemini-1.5-flash-002', + Icon: LLM_PROVIDER_ICONS[LLMProviderType.GOOGLE], + }, + { + value: LLMProviderType.AZURE, + label: 'Azure', + baseUrlPlaceholder: 'https://{your-resource-name}.openai.azure.com', + modelsPlaceholder: 'gpt-4,gpt-35-turbo', + Icon: LLM_PROVIDER_ICONS[LLMProviderType.AZURE], }, { value: LLMProviderType.COHERE, label: 'Cohere', baseUrlPlaceholder: 'https://api.cohere.ai/v1', - modelsPlaceholder: 'command-r, command-r-plus, command-r-plus-online', + modelsPlaceholder: 'command-r,command-r-plus,command-r-plus-online', + Icon: LLM_PROVIDER_ICONS[LLMProviderType.COHERE], }, { value: LLMProviderType.MISTRAL, label: 'Mistral', baseUrlPlaceholder: 'https://api.mistral.ai/v1', - modelsPlaceholder: 'mistral-large-latest, open-mistral-nemo', + modelsPlaceholder: 'mistral-large-latest,open-mistral-nemo', + Icon: LLM_PROVIDER_ICONS[LLMProviderType.MISTRAL], }, ] as const; diff --git a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/new-llm-provider-form.tsx b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/new-llm-provider-form.tsx index ab8e18f2fe..ab9bddaa36 100644 --- a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/new-llm-provider-form.tsx +++ b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/new-llm-provider-form.tsx @@ -180,9 +180,12 @@ export const LLMProviderForm = ({ onAdd, value, onChange }: LLMProviderFormProps - {LLM_PROVIDERS.map((provider) => ( - - {provider.label} + {LLM_PROVIDERS.map(({ value, label, Icon }) => ( + +
+ + {label} +
))}
diff --git a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/util.ts b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/util.ts new file mode 100644 index 0000000000..0391a52591 --- /dev/null +++ b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/util.ts @@ -0,0 +1,13 @@ +import type { LLMProvider } from '@teable/openapi'; + +export const generateModelKeyList = (llmProviders: LLMProvider[]) => { + return llmProviders + .map(({ models, type, name }) => models.split(',').map((model) => `${type}@${model}@${name}`)) + .flat(); +}; + +export const parseModelKey = (modelKey: string | undefined) => { + if (!modelKey) return {}; + const [type, model, name] = modelKey.split('@'); + return { type, model, name }; +}; diff --git a/packages/common-i18n/src/locales/zh/common.json b/packages/common-i18n/src/locales/zh/common.json index c4901e01ce..8719907058 100644 --- a/packages/common-i18n/src/locales/zh/common.json +++ b/packages/common-i18n/src/locales/zh/common.json @@ -243,7 +243,7 @@ "aiSettings": "AI 配置", "ai": { "name": "名称", - "nameDescription": "模型提供商的名称", + "nameDescription": "名称可自定义,用于区分不同的模型提供商", "enable": "启用 AI", "enableDescription": "启用当前实例的 AI 功能,所有用户将能够使用 AI 功能", "updateLLMProvider": "更新模型提供商", From 36fd379de095e14d378d32261aec059200b24475 Mon Sep 17 00:00:00 2001 From: SkyHuang Date: Fri, 27 Dec 2024 18:56:39 +0800 Subject: [PATCH 16/20] chore: add slider component --- packages/ui-lib/package.json | 1 + packages/ui-lib/src/shadcn/index.ts | 1 + packages/ui-lib/src/shadcn/ui/slider.tsx | 25 ++++++++ pnpm-lock.yaml | 77 ++++++++++++++++++++++-- 4 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 packages/ui-lib/src/shadcn/ui/slider.tsx diff --git a/packages/ui-lib/package.json b/packages/ui-lib/package.json index 7f77fa8214..50937ddf53 100644 --- a/packages/ui-lib/package.json +++ b/packages/ui-lib/package.json @@ -133,6 +133,7 @@ "@radix-ui/react-scroll-area": "1.0.5", "@radix-ui/react-select": "2.0.0", "@radix-ui/react-separator": "1.0.3", + "@radix-ui/react-slider": "1.2.2", "@radix-ui/react-slot": "1.0.2", "@radix-ui/react-switch": "1.0.3", "@radix-ui/react-tabs": "1.0.4", diff --git a/packages/ui-lib/src/shadcn/index.ts b/packages/ui-lib/src/shadcn/index.ts index 95c66ff997..deb55ec60d 100644 --- a/packages/ui-lib/src/shadcn/index.ts +++ b/packages/ui-lib/src/shadcn/index.ts @@ -39,3 +39,4 @@ export * from './ui/toggle-group'; export * from './ui/chart'; export * from './ui/input-otp'; export * from './ui/breadcrumb'; +export * from './ui/slider'; diff --git a/packages/ui-lib/src/shadcn/ui/slider.tsx b/packages/ui-lib/src/shadcn/ui/slider.tsx new file mode 100644 index 0000000000..a1a0088a38 --- /dev/null +++ b/packages/ui-lib/src/shadcn/ui/slider.tsx @@ -0,0 +1,25 @@ +'use client'; + +import * as SliderPrimitive from '@radix-ui/react-slider'; +import * as React from 'react'; + +import { cn } from '../utils'; + +const Slider = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + + +)); +Slider.displayName = SliderPrimitive.Root.displayName; + +export { Slider }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 639dc909d2..25bfcc7c10 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1715,6 +1715,9 @@ importers: '@radix-ui/react-separator': specifier: 1.0.3 version: 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.69)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slider': + specifier: 1.2.2 + version: 1.2.2(@types/react-dom@18.2.22)(@types/react@18.2.69)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-slot': specifier: 1.0.2 version: 1.0.2(@types/react@18.2.69)(react@18.3.1) @@ -5217,6 +5220,9 @@ packages: '@radix-ui/number@1.0.1': resolution: {integrity: sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==} + '@radix-ui/number@1.1.0': + resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==} + '@radix-ui/primitive@1.0.1': resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} @@ -5772,6 +5778,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-slider@1.2.2': + resolution: {integrity: sha512-sNlU06ii1/ZcbHf8I9En54ZPW0Vil/yPVg4vQMcFNjrIx51jsHbFl1HYHQvCIWJSr1q0ZmA+iIs/ZTv8h7HHSA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-slot@1.0.2': resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} peerDependencies: @@ -5975,6 +5994,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-previous@1.1.0': + resolution: {integrity: sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-rect@1.0.1': resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==} peerDependencies: @@ -5993,6 +6021,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-size@1.1.0': + resolution: {integrity: sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-visually-hidden@1.0.3': resolution: {integrity: sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==} peerDependencies: @@ -21172,6 +21209,8 @@ snapshots: dependencies: '@babel/runtime': 7.26.0 + '@radix-ui/number@1.1.0': {} + '@radix-ui/primitive@1.0.1': dependencies: '@babel/runtime': 7.26.0 @@ -21768,6 +21807,25 @@ snapshots: '@types/react': 18.2.69 '@types/react-dom': 18.2.22 + '@radix-ui/react-slider@1.2.2(@types/react-dom@18.2.22)(@types/react@18.2.69)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/number': 1.1.0 + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-collection': 1.1.1(@types/react-dom@18.2.22)(@types/react@18.2.69)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.69)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.69)(react@18.3.1) + '@radix-ui/react-direction': 1.1.0(@types/react@18.2.69)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.2.22)(@types/react@18.2.69)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.69)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.69)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.2.69)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.2.69)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.2.69 + '@types/react-dom': 18.2.22 + '@radix-ui/react-slot@1.0.2(@types/react@18.2.69)(react@18.3.1)': dependencies: '@babel/runtime': 7.26.0 @@ -21974,6 +22032,12 @@ snapshots: optionalDependencies: '@types/react': 18.2.69 + '@radix-ui/react-use-previous@1.1.0(@types/react@18.2.69)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.2.69 + '@radix-ui/react-use-rect@1.0.1(@types/react@18.2.69)(react@18.3.1)': dependencies: '@babel/runtime': 7.26.0 @@ -21990,6 +22054,13 @@ snapshots: optionalDependencies: '@types/react': 18.2.69 + '@radix-ui/react-use-size@1.1.0(@types/react@18.2.69)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.69)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.2.69 + '@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.69)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.26.0 @@ -25757,13 +25828,13 @@ snapshots: axios@0.21.4: dependencies: - follow-redirects: 1.15.9 + follow-redirects: 1.15.9(debug@4.4.0) transitivePeerDependencies: - debug axios@1.7.7: dependencies: - follow-redirects: 1.15.9 + follow-redirects: 1.15.9(debug@4.4.0) form-data: 4.0.0 proxy-from-env: 1.1.0 transitivePeerDependencies: @@ -28490,8 +28561,6 @@ snapshots: fn.name@1.1.0: {} - follow-redirects@1.15.9: {} - follow-redirects@1.15.9(debug@4.4.0): optionalDependencies: debug: 4.4.0 From f703a0f90a562c673acf5b1b76acd2a112e72877 Mon Sep 17 00:00:00 2001 From: SkyHuang Date: Thu, 2 Jan 2025 15:36:42 +0800 Subject: [PATCH 17/20] feat: distinguish sensitive fields of the setting api --- .../src/features/ai/ai.service.ts | 40 ++++++++++---- .../features/setting/setting.controller.ts | 42 +++++++------- .../src/backend/api/rest/table.ssr.ts | 6 ++ .../components/ai-config/ai-model-select.tsx | 4 +- .../setting/components/ai-config/util.ts | 4 +- .../src/features/app/hooks/useAI.ts | 2 +- .../src/features/app/hooks/useSetting.ts | 6 +- .../openapi/src/admin/setting/get-public.ts | 55 +++++++++++++++++++ packages/openapi/src/admin/setting/get.ts | 31 ----------- packages/openapi/src/admin/setting/index.ts | 1 + 10 files changed, 122 insertions(+), 69 deletions(-) create mode 100644 packages/openapi/src/admin/setting/get-public.ts diff --git a/apps/nestjs-backend/src/features/ai/ai.service.ts b/apps/nestjs-backend/src/features/ai/ai.service.ts index de7fc05bc7..14d55aacc5 100644 --- a/apps/nestjs-backend/src/features/ai/ai.service.ts +++ b/apps/nestjs-backend/src/features/ai/ai.service.ts @@ -5,6 +5,7 @@ import { createGoogleGenerativeAI } from '@ai-sdk/google'; import { createMistral } from '@ai-sdk/mistral'; import { createOpenAI } from '@ai-sdk/openai'; import { Injectable } from '@nestjs/common'; +import type { LLMProvider } from '@teable/openapi'; import { LLMProviderType } from '@teable/openapi'; import { streamText } from 'ai'; import { SettingService } from '../setting/setting.service'; @@ -24,13 +25,14 @@ export class AiService { [LLMProviderType.MISTRAL]: createMistral, } as const; - async getModelConfig(task: Task) { - const { aiConfig } = await this.settingService.getSetting(); - // aiConfig?.codingModel type@model@provider - const currentTaskModel = TASK_MODEL_MAP[task]; - const [type, model, provider] = - (aiConfig?.[currentTaskModel as keyof typeof aiConfig] as string)?.split('@') || []; - const llmProviders = aiConfig?.llmProviders || []; + public parseModelKey(modelKey: string) { + const [type, model, provider] = modelKey.split('@'); + return { type, model, provider }; + } + + // modelKey-> type@model@provider + async getModelConfig(modelKey: string, llmProviders: LLMProvider[] = []) { + const { type, model, provider } = this.parseModelKey(modelKey); const providerConfig = llmProviders.find( (p) => @@ -53,11 +55,12 @@ export class AiService { } async getModelInstance( - task: Task + modelKey: string, + llmProviders: LLMProvider[] = [] ): Promise< ReturnType> > { - const { type, model, baseUrl, apiKey } = await this.getModelConfig(task); + const { type, model, baseUrl, apiKey } = await this.getModelConfig(modelKey, llmProviders); if (!baseUrl || !apiKey) { throw new Error('AI configuration is not set'); @@ -77,8 +80,25 @@ export class AiService { })(model); } + async getAIConfig() { + const { aiConfig } = await this.settingService.getSetting(); + + if (!aiConfig) { + throw new Error('AI configuration is not set'); + } + + if (!aiConfig.enable) { + throw new Error('AI is not enabled'); + } + + return aiConfig; + } + async generate(prompt: string, task: Task = Task.Coding) { - const modelInstance = await this.getModelInstance(task); + const config = await this.getAIConfig(); + const currentTaskModel = TASK_MODEL_MAP[task]; + const modelKey = config[currentTaskModel as keyof typeof config] as string; + const modelInstance = await this.getModelInstance(modelKey, config.llmProviders); return await streamText({ model: modelInstance, diff --git a/apps/nestjs-backend/src/features/setting/setting.controller.ts b/apps/nestjs-backend/src/features/setting/setting.controller.ts index 62d2ce4938..7c6ac51185 100644 --- a/apps/nestjs-backend/src/features/setting/setting.controller.ts +++ b/apps/nestjs-backend/src/features/setting/setting.controller.ts @@ -1,5 +1,5 @@ import { Body, Controller, Get, Patch } from '@nestjs/common'; -import type { ISettingVo } from '@teable/openapi'; +import type { IPublicSettingVo, ISettingVo } from '@teable/openapi'; import { IUpdateSettingRo, updateSettingRoSchema } from '@teable/openapi'; import { ZodValidationPipe } from '../../zod.validation.pipe'; import { Permissions } from '../auth/decorators/permissions.decorator'; @@ -19,38 +19,38 @@ export class SettingController { return await this.settingService.getSetting(); } - @Patch() - @Permissions('instance|update') - async updateSetting( - @Body(new ZodValidationPipe(updateSettingRoSchema)) - updateSettingRo: IUpdateSettingRo - ): Promise { - const res = await this.settingService.updateSetting(updateSettingRo); - return { - ...res, - aiConfig: res.aiConfig ? JSON.parse(res.aiConfig) : null, - }; - } - /** * Public endpoint for getting public settings without authentication */ @Public() @Get('public') - async getPublicSetting(): Promise< - Pick & { - aiConfig: { - enable: boolean; - }; - } - > { + async getPublicSetting(): Promise { const setting = await this.settingService.getSetting(); const { aiConfig, ...rest } = setting; return { ...rest, aiConfig: { enable: aiConfig?.enable ?? false, + llmProviders: + aiConfig?.llmProviders?.map((provider) => ({ + type: provider.type, + name: provider.name, + models: provider.models, + })) ?? [], }, }; } + + @Patch() + @Permissions('instance|update') + async updateSetting( + @Body(new ZodValidationPipe(updateSettingRoSchema)) + updateSettingRo: IUpdateSettingRo + ): Promise { + const res = await this.settingService.updateSetting(updateSettingRo); + return { + ...res, + aiConfig: res.aiConfig ? JSON.parse(res.aiConfig) : null, + }; + } } diff --git a/apps/nextjs-app/src/backend/api/rest/table.ssr.ts b/apps/nextjs-app/src/backend/api/rest/table.ssr.ts index be8054ef94..9c9e3a45fb 100644 --- a/apps/nextjs-app/src/backend/api/rest/table.ssr.ts +++ b/apps/nextjs-app/src/backend/api/rest/table.ssr.ts @@ -19,6 +19,7 @@ import type { IGroupPointsRo, IGroupPointsVo, ListSpaceCollaboratorRo, + IPublicSettingVo, } from '@teable/openapi'; import { ACCEPT_INVITATION_LINK, @@ -27,6 +28,7 @@ import { GET_DEFAULT_VIEW_ID, GET_FIELD_LIST, GET_GROUP_POINTS, + GET_PUBLIC_SETTING, GET_RECORDS_URL, GET_RECORD_URL, GET_SETTING, @@ -168,6 +170,10 @@ export class SsrApi { return this.axios.get(GET_SETTING).then(({ data }) => data); } + async getPublicSetting() { + return this.axios.get(GET_PUBLIC_SETTING).then(({ data }) => data); + } + async getUserMe() { return this.axios.get(USER_ME).then(({ data }) => data); } diff --git a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-model-select.tsx b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-model-select.tsx index e1216078a0..0071c11353 100644 --- a/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-model-select.tsx +++ b/apps/nextjs-app/src/features/app/blocks/admin/setting/components/ai-config/ai-model-select.tsx @@ -25,6 +25,7 @@ interface IAIModelSelectProps { size?: 'xs' | 'sm' | 'lg' | 'default' | null | undefined; className?: string; options?: string[]; + disabled?: boolean; } export function AIModelSelect({ @@ -33,6 +34,7 @@ export function AIModelSelect({ size = 'default', className, options = [], + disabled, }: IAIModelSelectProps) { const [open, setOpen] = React.useState(false); const currentModel = options.find((model) => model.toLowerCase() === value.toLowerCase()); @@ -43,7 +45,7 @@ export function AIModelSelect({ return ( - +