Compare commits
103 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
2c1daebf74 | |
|
|
cd2c561547 | |
|
|
8d8b704a73 | |
|
|
501b249a7c | |
|
|
a522fe3498 | |
|
|
798faf00d4 | |
|
|
e86859fc6e | |
|
|
74fc21608d | |
|
|
7918c2b7b8 | |
|
|
191af7e22d | |
|
|
5f65a8dda3 | |
|
|
9063270608 | |
|
|
907527ecf7 | |
|
|
bd58362612 | |
|
|
2c96c983b5 | |
|
|
6ee23bebb5 | |
|
|
f17d6aca22 | |
|
|
a6e77c12fe | |
|
|
e6164160ef | |
|
|
b7941a45b9 | |
|
|
fa170db990 | |
|
|
e1d22386a1 | |
|
|
8c9b6ec07e | |
|
|
0f5d9b9b68 | |
|
|
43b4279e2d | |
|
|
6ee913f067 | |
|
|
b0bdbf633e | |
|
|
3f2f319e3b | |
|
|
7237a55c37 | |
|
|
9eaf30c617 | |
|
|
665ad78b20 | |
|
|
36c57c6a41 | |
|
|
1730caf144 | |
|
|
5ead504e66 | |
|
|
673308a513 | |
|
|
03daddd2fa | |
|
|
0280845a3a | |
|
|
9d7b8c7c1d | |
|
|
4e169bf3d6 | |
|
|
e04a84e08a | |
|
|
ec484afcaa | |
|
|
035a93a439 | |
|
|
936d7c298d | |
|
|
f0e6baa352 | |
|
|
b7498e3bb2 | |
|
|
467884c0df | |
|
|
285ac31ceb | |
|
|
5e475801a4 | |
|
|
2d757057c1 | |
|
|
b9a40d26ce | |
|
|
b1ce04b5d6 | |
|
|
10fa1637ea | |
|
|
de91c632e0 | |
|
|
6794ebf5c1 | |
|
|
be0662d918 | |
|
|
41dc3ca5fa | |
|
|
4e9122a657 | |
|
|
765f61d444 | |
|
|
b3c0b0d38f | |
|
|
b7e53489d2 | |
|
|
adceadc16c | |
|
|
56b919390d | |
|
|
e02b4954ee | |
|
|
7a841d8171 | |
|
|
66a9865c52 | |
|
|
c174f7a262 | |
|
|
b82018e971 | |
|
|
57382b2ab7 | |
|
|
5b3dabacd0 | |
|
|
b81cd6e3d0 | |
|
|
e8f542ef63 | |
|
|
6f09892b13 | |
|
|
c54328b2c8 | |
|
|
40a458989b | |
|
|
72abcbeb26 | |
|
|
9b8d9c0db4 | |
|
|
df7af82d85 | |
|
|
ed7afaafb6 | |
|
|
c62109c022 | |
|
|
ad5fb2fa7b | |
|
|
a1a251f2fe | |
|
|
fc910862d0 | |
|
|
fbaa2800c6 | |
|
|
ac99f5670c | |
|
|
6a47033ab2 | |
|
|
7fcd5a3f41 | |
|
|
488a3dfe05 | |
|
|
343350e302 | |
|
|
78aa17b367 | |
|
|
c4b59f3292 | |
|
|
8b77ede8d5 | |
|
|
f95bf09652 | |
|
|
bea9a580c5 | |
|
|
ccac741f70 | |
|
|
30e1cde3b2 | |
|
|
f055fe609c | |
|
|
1600c29f44 | |
|
|
7cb1865183 | |
|
|
030990bb08 | |
|
|
7973bc9d40 | |
|
|
8c597005d6 | |
|
|
d64d64ad4f | |
|
|
b8bd679fe4 |
|
|
@ -355,7 +355,7 @@ RUN chmod +x /supabase/postgres/wrapper.sh /supabase/postgrest/wrapper.sh /supab
|
||||||
ENTRYPOINT ["supervisord"]
|
ENTRYPOINT ["supervisord"]
|
||||||
|
|
||||||
#HEALTHCHECK --interval=2s --timeout=2s --retries=10 CMD pg_isready -U postgres -h localhost
|
#HEALTHCHECK --interval=2s --timeout=2s --retries=10 CMD pg_isready -U postgres -h localhost
|
||||||
HEALTHCHECK --interval=5s --timeout=3s --start-period=30s --retries=10 CMD pg_isready -U postgres -h 127.0.0.1 -p 5432 || exit 1
|
HEALTHCHECK --interval=10s --timeout=6s --start-period=60s --retries=5 CMD pg_isready -U postgres -h 127.0.0.1 -p 5432 || exit 1
|
||||||
STOPSIGNAL SIGINT
|
STOPSIGNAL SIGINT
|
||||||
#EXPOSE 5432
|
#EXPOSE 5432
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ If the environment variable is set, it will disable the input in the user settin
|
||||||
|
|
||||||
In the 1st migration file `supabase/migrations/20240108234540_setup.sql` you will need to replace 2 values with the values you got above:
|
In the 1st migration file `supabase/migrations/20240108234540_setup.sql` you will need to replace 2 values with the values you got above:
|
||||||
|
|
||||||
- `project_url` (line 53): `http://supabase_kong_chatbotui:8000` (default) can remain unchanged if you don't change your `project_id` in the `config.toml` file
|
- `project_url` (line 53): `http://localhost:8000` (default) can remain unchanged if you don't change your `project_id` in the `config.toml` file
|
||||||
- `service_role_key` (line 54): You got this value from running `supabase status`
|
- `service_role_key` (line 54): You got this value from running `supabase status`
|
||||||
|
|
||||||
This prevents issues with storage files not being deleted properly.
|
This prevents issues with storage files not being deleted properly.
|
||||||
|
|
|
||||||
|
|
@ -169,12 +169,19 @@ export default function WorkspaceLayout({ children }: WorkspaceLayoutProps) {
|
||||||
setTools(toolData.tools)
|
setTools(toolData.tools)
|
||||||
|
|
||||||
const modelData = await getModelWorkspacesByWorkspaceId(workspaceId)
|
const modelData = await getModelWorkspacesByWorkspaceId(workspaceId)
|
||||||
|
//console.log("......Model data from DB:", modelData.models)
|
||||||
setModels(modelData.models)
|
setModels(modelData.models)
|
||||||
|
|
||||||
|
// setChatSettings({
|
||||||
|
// model: (searchParams.get("model") ||
|
||||||
|
// workspace?.default_model ||
|
||||||
|
// "gpt-4-1106-preview") as LLMID,
|
||||||
|
|
||||||
|
const firstModel = modelData.models[0]?.model_id || "GPT"; // 默认兜底
|
||||||
setChatSettings({
|
setChatSettings({
|
||||||
model: (searchParams.get("model") ||
|
model: (searchParams.get("model") ||
|
||||||
workspace?.default_model ||
|
workspace?.default_model ||
|
||||||
"gpt-4-1106-preview") as LLMID,
|
firstModel) as LLMID,
|
||||||
prompt:
|
prompt:
|
||||||
workspace?.default_prompt ||
|
workspace?.default_prompt ||
|
||||||
t("chat.promptPlaceholder"),
|
t("chat.promptPlaceholder"),
|
||||||
|
|
@ -184,7 +191,7 @@ export default function WorkspaceLayout({ children }: WorkspaceLayoutProps) {
|
||||||
includeWorkspaceInstructions:
|
includeWorkspaceInstructions:
|
||||||
workspace?.include_workspace_instructions || true,
|
workspace?.include_workspace_instructions || true,
|
||||||
embeddingsProvider:
|
embeddingsProvider:
|
||||||
(workspace?.embeddings_provider as "openai" | "local") || "openai"
|
(workspace?.embeddings_provider as "openai" | "local" | "bge-m3") || "bge-m3"
|
||||||
})
|
})
|
||||||
|
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import { Providers } from "@/components/utility/providers"
|
||||||
import TranslationsProvider from "@/components/utility/translations-provider"
|
import TranslationsProvider from "@/components/utility/translations-provider"
|
||||||
import initTranslations from "@/lib/i18n"
|
import initTranslations from "@/lib/i18n"
|
||||||
import { Database } from "@/supabase/types"
|
import { Database } from "@/supabase/types"
|
||||||
//import { createServerClient } from "@supabase/ssr"
|
|
||||||
import { getSupabaseServerClient } from "@/lib/supabase/server"
|
import { getSupabaseServerClient } from "@/lib/supabase/server"
|
||||||
import { Metadata, Viewport } from "next"
|
import { Metadata, Viewport } from "next"
|
||||||
import { Inter } from "next/font/google"
|
import { Inter } from "next/font/google"
|
||||||
|
|
@ -114,39 +113,22 @@ export default async function RootLayout({
|
||||||
console.log(`🍪 Cookie: ${cookie.name} = ${cookie.value}`);
|
console.log(`🍪 Cookie: ${cookie.name} = ${cookie.value}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// const supabaseUrl = getRuntimeEnv("SUPABASE_URL");
|
|
||||||
// console.log("==========>Supabase URL: ", supabaseUrl);
|
|
||||||
|
|
||||||
|
|
||||||
let supabaseUrl = "";
|
let supabaseUrl = "";
|
||||||
try {
|
try {
|
||||||
// 等待直到获取到有效的 Supabase URL
|
// 等待直到获取到有效的 Supabase URL
|
||||||
supabaseUrl = await getValidSupabaseUrl();
|
supabaseUrl = await getValidSupabaseUrl();
|
||||||
console.log("==========>获取到有效的 Supabase URL: ", supabaseUrl);
|
//console.log("==========>获取到有效的 Supabase URL: ", supabaseUrl);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Supabase URL 获取失败:", error);
|
console.error("Supabase URL 获取失败:", error);
|
||||||
return <div>Failed to fetch Supabase configuration, please try again later.</div>; // 出现错误时返回一个友好的提示
|
return <div>Failed to fetch Supabase configuration, please try again later.</div>; // 出现错误时返回一个友好的提示
|
||||||
}
|
}
|
||||||
|
|
||||||
// const supabase = createServerClient<Database>(
|
|
||||||
// getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000",
|
|
||||||
// process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
||||||
// {
|
|
||||||
// cookies: {
|
|
||||||
// get(name: string) {
|
|
||||||
// return cookieStore.get(name)?.value
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// )
|
|
||||||
|
|
||||||
const supabase = getSupabaseServerClient()
|
const supabase = getSupabaseServerClient()
|
||||||
// const session = (await supabase.auth.getSession()).data.session
|
|
||||||
const { data, error } = await supabase.auth.getSession();
|
const { data, error } = await supabase.auth.getSession();
|
||||||
if (error) {
|
if (error) {
|
||||||
console.log("[layout.tsx]............Session Error: ", error);
|
console.log("[layout.tsx]............Session Error: ", error);
|
||||||
} else {
|
} else {
|
||||||
console.log("[layout.tsx]............Session Data: ", data.session);
|
//console.log("[layout.tsx]............Session Data: ", data.session);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { t, resources } = await initTranslations(locale, i18nNamespaces)
|
const { t, resources } = await initTranslations(locale, i18nNamespaces)
|
||||||
|
|
@ -179,20 +161,4 @@ export default async function RootLayout({
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
)
|
)
|
||||||
|
|
||||||
// return (
|
|
||||||
// <Providers attribute="class" defaultTheme="dark">
|
|
||||||
// <TranslationsProvider
|
|
||||||
// namespaces={i18nNamespaces}
|
|
||||||
// locale={locale}
|
|
||||||
// resources={resources}
|
|
||||||
// >
|
|
||||||
// <Toaster richColors position="top-center" duration={3000} />
|
|
||||||
// <div className={`${inter.className} bg-background text-foreground flex h-dvh flex-col items-center overflow-x-auto`}>
|
|
||||||
// {data.session ? <GlobalState>{children}</GlobalState> : children}
|
|
||||||
// </div>
|
|
||||||
// </TranslationsProvider>
|
|
||||||
// </Providers>
|
|
||||||
// )
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,15 +15,13 @@ export default function HomePage() {
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const { t, i18n } = useTranslation()
|
const { t, i18n } = useTranslation()
|
||||||
|
|
||||||
const [preferredLanguage, setPreferredLanguage] = useState<string>('en') // 默认语言为 'en'
|
const [preferredLanguage, setPreferredLanguage] = useState<string>('en')
|
||||||
|
|
||||||
// 根据 localStorage 或 cookie 设置 preferredLanguage
|
// 根据 localStorage 或 cookie 设置 preferredLanguage
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const languageFromStorage = localStorage.getItem('preferred-language') || document.cookie.split('; ').find(row => row.startsWith('preferred-language='))?.split('=')[1];
|
const languageFromStorage = localStorage.getItem('preferred-language') || document.cookie.split('; ').find(row => row.startsWith('preferred-language='))?.split('=')[1];
|
||||||
if (languageFromStorage) {
|
if (languageFromStorage) {
|
||||||
setPreferredLanguage(languageFromStorage);
|
setPreferredLanguage(languageFromStorage);
|
||||||
// 更新 i18n 的语言设置
|
|
||||||
//i18n.changeLanguage(languageFromStorage); // 通过 i18n 更新默认语言
|
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,11 @@ import {
|
||||||
SETUP_STEP_COUNT,
|
SETUP_STEP_COUNT,
|
||||||
StepContainer
|
StepContainer
|
||||||
} from "../../../components/setup/step-container"
|
} from "../../../components/setup/step-container"
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
|
|
||||||
import { usePathname } from "next/navigation"
|
import { usePathname } from "next/navigation"
|
||||||
import i18nConfig from "@/i18nConfig"
|
import i18nConfig from "@/i18nConfig"
|
||||||
|
|
||||||
|
import { createModel } from "@/db/models"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
export default function SetupPage() {
|
export default function SetupPage() {
|
||||||
const {
|
const {
|
||||||
|
|
@ -39,6 +39,7 @@ export default function SetupPage() {
|
||||||
} = useContext(ChatbotUIContext)
|
} = useContext(ChatbotUIContext)
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -58,8 +59,6 @@ export default function SetupPage() {
|
||||||
const getLocalizedPath = (subPath: string) =>
|
const getLocalizedPath = (subPath: string) =>
|
||||||
locale === defaultLocale ? `/${subPath}` : `/${locale}/${subPath}`
|
locale === defaultLocale ? `/${subPath}` : `/${locale}/${subPath}`
|
||||||
|
|
||||||
const { t } = useTranslation()
|
|
||||||
|
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
const [currentStep, setCurrentStep] = useState(1)
|
const [currentStep, setCurrentStep] = useState(1)
|
||||||
|
|
@ -86,6 +85,10 @@ export default function SetupPage() {
|
||||||
const [perplexityAPIKey, setPerplexityAPIKey] = useState("")
|
const [perplexityAPIKey, setPerplexityAPIKey] = useState("")
|
||||||
const [openrouterAPIKey, setOpenrouterAPIKey] = useState("")
|
const [openrouterAPIKey, setOpenrouterAPIKey] = useState("")
|
||||||
|
|
||||||
|
// 只允许点击一次
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
;(async () => {
|
;(async () => {
|
||||||
//const supabaseClient = await supabase(); // Await the client creation
|
//const supabaseClient = await supabase(); // Await the client creation
|
||||||
|
|
@ -93,10 +96,8 @@ export default function SetupPage() {
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
// 强制跳转到带有 locale 的 login 页面
|
// 强制跳转到带有 locale 的 login 页面
|
||||||
console.log("...........[setup/page.tsx]")
|
//console.log("...........[setup/page.tsx]")
|
||||||
//return router.push(`${homePath}/login`)
|
|
||||||
return router.push(getLocalizedPath("login"))
|
return router.push(getLocalizedPath("login"))
|
||||||
// return router.push(`/${locale}/login`)
|
|
||||||
} else {
|
} else {
|
||||||
const user = session.user
|
const user = session.user
|
||||||
|
|
||||||
|
|
@ -123,8 +124,6 @@ export default function SetupPage() {
|
||||||
const homeWorkspaceId = await getHomeWorkspaceByUserId(
|
const homeWorkspaceId = await getHomeWorkspaceByUserId(
|
||||||
session.user.id
|
session.user.id
|
||||||
)
|
)
|
||||||
//return router.push(`${homePath}/${homeWorkspaceId}/chat`)
|
|
||||||
// return router.push(`/${locale}/${homeWorkspaceId}/chat`)
|
|
||||||
return router.push(getLocalizedPath(`${homeWorkspaceId}/chat`))
|
return router.push(getLocalizedPath(`${homeWorkspaceId}/chat`))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -134,7 +133,10 @@ export default function SetupPage() {
|
||||||
const handleShouldProceed = (proceed: boolean) => {
|
const handleShouldProceed = (proceed: boolean) => {
|
||||||
if (proceed) {
|
if (proceed) {
|
||||||
if (currentStep === SETUP_STEP_COUNT) {
|
if (currentStep === SETUP_STEP_COUNT) {
|
||||||
handleSaveSetupSetting()
|
//handleSaveSetupSetting()
|
||||||
|
if (!isSubmitting) {
|
||||||
|
handleSaveSetupSetting()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setCurrentStep(currentStep + 1)
|
setCurrentStep(currentStep + 1)
|
||||||
}
|
}
|
||||||
|
|
@ -143,56 +145,149 @@ export default function SetupPage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const handleSaveSetupSetting = async () => {
|
||||||
|
|
||||||
|
// const session = (await supabase.auth.getSession()).data.session
|
||||||
|
// if (!session) {
|
||||||
|
// return router.push(getLocalizedPath("login"))
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const user = session.user
|
||||||
|
// const profile = await getProfileByUserId(user.id)
|
||||||
|
|
||||||
|
// const updateProfilePayload: TablesUpdate<"profiles"> = {
|
||||||
|
// ...profile,
|
||||||
|
// has_onboarded: true,
|
||||||
|
// display_name: displayName,
|
||||||
|
// username,
|
||||||
|
// openai_api_key: openaiAPIKey,
|
||||||
|
// openai_organization_id: openaiOrgID,
|
||||||
|
// anthropic_api_key: anthropicAPIKey,
|
||||||
|
// google_gemini_api_key: googleGeminiAPIKey,
|
||||||
|
// mistral_api_key: mistralAPIKey,
|
||||||
|
// groq_api_key: groqAPIKey,
|
||||||
|
// perplexity_api_key: perplexityAPIKey,
|
||||||
|
// openrouter_api_key: openrouterAPIKey,
|
||||||
|
// use_azure_openai: useAzureOpenai,
|
||||||
|
// azure_openai_api_key: azureOpenaiAPIKey,
|
||||||
|
// azure_openai_endpoint: azureOpenaiEndpoint,
|
||||||
|
// azure_openai_35_turbo_id: azureOpenai35TurboID,
|
||||||
|
// azure_openai_45_turbo_id: azureOpenai45TurboID,
|
||||||
|
// azure_openai_45_vision_id: azureOpenai45VisionID,
|
||||||
|
// azure_openai_embeddings_id: azureOpenaiEmbeddingsID
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const updatedProfile = await updateProfile(profile.id, updateProfilePayload)
|
||||||
|
// setProfile(updatedProfile)
|
||||||
|
|
||||||
|
// const workspaces = await getWorkspacesByUserId(profile.user_id)
|
||||||
|
// const homeWorkspace = workspaces.find(w => w.is_home)
|
||||||
|
|
||||||
|
// if (!homeWorkspace) {
|
||||||
|
// throw new Error("Home workspace not found for user during setup. This should not happen.")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const baseUrl = typeof window !== "undefined"
|
||||||
|
// ? `http://${window.location.hostname}:30000/v1`
|
||||||
|
// : "http://localhost:30000/v1"
|
||||||
|
|
||||||
|
// if (typeof window !== "undefined") {
|
||||||
|
// await createModel(
|
||||||
|
// {
|
||||||
|
// user_id: profile.user_id,
|
||||||
|
// name: "Default",
|
||||||
|
// model_id: "GPT",
|
||||||
|
// base_url: baseUrl,
|
||||||
|
// api_key: "token-abc123",
|
||||||
|
// description: "Default LLM model created during onboarding",
|
||||||
|
// context_length: 131072
|
||||||
|
// },
|
||||||
|
// homeWorkspace.id
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // There will always be a home workspace
|
||||||
|
// setSelectedWorkspace(homeWorkspace!)
|
||||||
|
// setWorkspaces(workspaces)
|
||||||
|
|
||||||
|
// return router.push(getLocalizedPath(`${homeWorkspace?.id}/chat`))
|
||||||
|
// }
|
||||||
|
|
||||||
const handleSaveSetupSetting = async () => {
|
const handleSaveSetupSetting = async () => {
|
||||||
//const supabaseClient = await supabase(); // Await the client creation
|
if (isSubmitting) return
|
||||||
const session = (await supabase.auth.getSession()).data.session
|
setIsSubmitting(true)
|
||||||
if (!session) {
|
|
||||||
// return router.push(`/${locale}/login`)
|
try {
|
||||||
//return (`${homePath}/login`)
|
const session = (await supabase.auth.getSession()).data.session
|
||||||
return router.push(getLocalizedPath("login"))
|
if (!session) {
|
||||||
|
return router.push(getLocalizedPath("login"))
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = session.user
|
||||||
|
const profile = await getProfileByUserId(user.id)
|
||||||
|
|
||||||
|
const updateProfilePayload: TablesUpdate<"profiles"> = {
|
||||||
|
...profile,
|
||||||
|
has_onboarded: true,
|
||||||
|
display_name: displayName,
|
||||||
|
username,
|
||||||
|
openai_api_key: openaiAPIKey,
|
||||||
|
openai_organization_id: openaiOrgID,
|
||||||
|
anthropic_api_key: anthropicAPIKey,
|
||||||
|
google_gemini_api_key: googleGeminiAPIKey,
|
||||||
|
mistral_api_key: mistralAPIKey,
|
||||||
|
groq_api_key: groqAPIKey,
|
||||||
|
perplexity_api_key: perplexityAPIKey,
|
||||||
|
openrouter_api_key: openrouterAPIKey,
|
||||||
|
use_azure_openai: useAzureOpenai,
|
||||||
|
azure_openai_api_key: azureOpenaiAPIKey,
|
||||||
|
azure_openai_endpoint: azureOpenaiEndpoint,
|
||||||
|
azure_openai_35_turbo_id: azureOpenai35TurboID,
|
||||||
|
azure_openai_45_turbo_id: azureOpenai45TurboID,
|
||||||
|
azure_openai_45_vision_id: azureOpenai45VisionID,
|
||||||
|
azure_openai_embeddings_id: azureOpenaiEmbeddingsID
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedProfile = await updateProfile(profile.id, updateProfilePayload)
|
||||||
|
setProfile(updatedProfile)
|
||||||
|
|
||||||
|
const workspaces = await getWorkspacesByUserId(profile.user_id)
|
||||||
|
const homeWorkspace = workspaces.find(w => w.is_home)
|
||||||
|
|
||||||
|
if (!homeWorkspace) {
|
||||||
|
throw new Error("Home workspace not found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseUrl = typeof window !== "undefined"
|
||||||
|
? `http://${window.location.hostname}:30000/v1`
|
||||||
|
: "http://localhost:30000/v1"
|
||||||
|
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
await createModel(
|
||||||
|
{
|
||||||
|
user_id: profile.user_id,
|
||||||
|
name: t("setup.Default"),
|
||||||
|
model_id: "GPT",
|
||||||
|
base_url: baseUrl,
|
||||||
|
api_key: "token-abc123",
|
||||||
|
description: t("setup.DefaultLLMModelCreatedDuringOnboarding"),
|
||||||
|
context_length: 131072
|
||||||
|
},
|
||||||
|
homeWorkspace.id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedWorkspace(homeWorkspace)
|
||||||
|
setWorkspaces(workspaces)
|
||||||
|
|
||||||
|
router.push(getLocalizedPath(`${homeWorkspace.id}/chat`))
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Setup failed:", err)
|
||||||
|
setIsSubmitting(false) // 失败后允许重试
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = session.user
|
|
||||||
const profile = await getProfileByUserId(user.id)
|
|
||||||
|
|
||||||
const updateProfilePayload: TablesUpdate<"profiles"> = {
|
|
||||||
...profile,
|
|
||||||
has_onboarded: true,
|
|
||||||
display_name: displayName,
|
|
||||||
username,
|
|
||||||
openai_api_key: openaiAPIKey,
|
|
||||||
openai_organization_id: openaiOrgID,
|
|
||||||
anthropic_api_key: anthropicAPIKey,
|
|
||||||
google_gemini_api_key: googleGeminiAPIKey,
|
|
||||||
mistral_api_key: mistralAPIKey,
|
|
||||||
groq_api_key: groqAPIKey,
|
|
||||||
perplexity_api_key: perplexityAPIKey,
|
|
||||||
openrouter_api_key: openrouterAPIKey,
|
|
||||||
use_azure_openai: useAzureOpenai,
|
|
||||||
azure_openai_api_key: azureOpenaiAPIKey,
|
|
||||||
azure_openai_endpoint: azureOpenaiEndpoint,
|
|
||||||
azure_openai_35_turbo_id: azureOpenai35TurboID,
|
|
||||||
azure_openai_45_turbo_id: azureOpenai45TurboID,
|
|
||||||
azure_openai_45_vision_id: azureOpenai45VisionID,
|
|
||||||
azure_openai_embeddings_id: azureOpenaiEmbeddingsID
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedProfile = await updateProfile(profile.id, updateProfilePayload)
|
|
||||||
setProfile(updatedProfile)
|
|
||||||
|
|
||||||
const workspaces = await getWorkspacesByUserId(profile.user_id)
|
|
||||||
const homeWorkspace = workspaces.find(w => w.is_home)
|
|
||||||
|
|
||||||
|
|
||||||
// There will always be a home workspace
|
|
||||||
setSelectedWorkspace(homeWorkspace!)
|
|
||||||
setWorkspaces(workspaces)
|
|
||||||
|
|
||||||
// return router.push(`${homePath}/${homeWorkspace?.id}/chat`)
|
|
||||||
// return router.push(`/${locale}/${homeWorkspace?.id}/chat`)
|
|
||||||
return router.push(getLocalizedPath(`${homeWorkspace?.id}/chat`))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const renderStep = (stepNum: number) => {
|
const renderStep = (stepNum: number) => {
|
||||||
switch (stepNum) {
|
switch (stepNum) {
|
||||||
// Profile Step
|
// Profile Step
|
||||||
|
|
@ -271,7 +366,9 @@ export default function SetupPage() {
|
||||||
stepNum={currentStep}
|
stepNum={currentStep}
|
||||||
stepTitle={t("setup.SetupComplete")}
|
stepTitle={t("setup.SetupComplete")}
|
||||||
onShouldProceed={handleShouldProceed}
|
onShouldProceed={handleShouldProceed}
|
||||||
showNextButton={true}
|
//showNextButton={true}
|
||||||
|
showNextButton={true} // 一直显示按钮
|
||||||
|
isSubmitting={isSubmitting} // 控制是否禁用
|
||||||
showBackButton={true}
|
showBackButton={true}
|
||||||
>
|
>
|
||||||
<FinishStep displayName={displayName} />
|
<FinishStep displayName={displayName} />
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,14 @@ import { createClient } from "@supabase/supabase-js"
|
||||||
import { NextResponse } from "next/server"
|
import { NextResponse } from "next/server"
|
||||||
import OpenAI from "openai"
|
import OpenAI from "openai"
|
||||||
import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入
|
import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入
|
||||||
|
import { generateBgeM3Embedding } from "@/lib/generate-bgem3-embedding"
|
||||||
|
|
||||||
export async function POST(req: Request) {
|
export async function POST(req: Request) {
|
||||||
const json = await req.json()
|
const json = await req.json()
|
||||||
const { text, fileId, embeddingsProvider, fileExtension } = json as {
|
const { text, fileId, embeddingsProvider, fileExtension } = json as {
|
||||||
text: string
|
text: string
|
||||||
fileId: string
|
fileId: string
|
||||||
embeddingsProvider: "openai" | "local"
|
embeddingsProvider: "openai" | "local" | "bge-m3"
|
||||||
fileExtension: string
|
fileExtension: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,6 +83,40 @@ export async function POST(req: Request) {
|
||||||
})
|
})
|
||||||
|
|
||||||
embeddings = await Promise.all(embeddingPromises)
|
embeddings = await Promise.all(embeddingPromises)
|
||||||
|
} else if (embeddingsProvider === "bge-m3"){
|
||||||
|
// 示例:调用你自己的 BGE-M3 API 或本地函数
|
||||||
|
const embeddingPromises = chunks.map(async (chunk, index) => {
|
||||||
|
try {
|
||||||
|
// return await generateBgeM3Embedding(chunk.content)
|
||||||
|
|
||||||
|
const result = await generateBgeM3Embedding(chunk.content)
|
||||||
|
|
||||||
|
if (!Array.isArray(result)) {
|
||||||
|
console.error(`......❌ Chunk ${index}: result is not an array`, result)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.length !== 1024) {
|
||||||
|
console.error(`......❌ Chunk ${index}: incorrect length: ${result.length}`)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.every(x => typeof x === "number")) {
|
||||||
|
console.error(`......❌ Chunk ${index}: contains non-numbers`, result)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
console.error(`Error generating BGE-M3 embedding for chunk: ${chunk}`, error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
embeddings = await Promise.all(embeddingPromises)
|
||||||
|
console.log(`......[embedding] 维度: ${embeddings.length}`);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const file_items = chunks.map((chunk, index) => ({
|
const file_items = chunks.map((chunk, index) => ({
|
||||||
|
|
@ -96,6 +131,11 @@ export async function POST(req: Request) {
|
||||||
local_embedding:
|
local_embedding:
|
||||||
embeddingsProvider === "local"
|
embeddingsProvider === "local"
|
||||||
? ((embeddings[index] || null) as any)
|
? ((embeddings[index] || null) as any)
|
||||||
|
: null,
|
||||||
|
bge_m3_embedding:
|
||||||
|
embeddingsProvider === "bge-m3" && embeddings[index] && embeddings[index].length === 1024
|
||||||
|
// ? (embeddings[index] || null) as any
|
||||||
|
? embeddings[index] as any
|
||||||
: null
|
: null
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { generateLocalEmbedding } from "@/lib/generate-local-embedding"
|
import { generateLocalEmbedding } from "@/lib/generate-local-embedding"
|
||||||
|
import { generateBgeM3Embedding } from "@/lib/generate-bgem3-embedding"
|
||||||
import {
|
import {
|
||||||
processCSV,
|
processCSV,
|
||||||
processJSON,
|
processJSON,
|
||||||
|
|
@ -15,6 +16,7 @@ import OpenAI from "openai"
|
||||||
import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入
|
import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入
|
||||||
|
|
||||||
export async function POST(req: Request) {
|
export async function POST(req: Request) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const supabaseAdmin = createClient<Database>(
|
const supabaseAdmin = createClient<Database>(
|
||||||
getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000",
|
getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000",
|
||||||
|
|
@ -28,6 +30,8 @@ export async function POST(req: Request) {
|
||||||
const file_id = formData.get("file_id") as string
|
const file_id = formData.get("file_id") as string
|
||||||
const embeddingsProvider = formData.get("embeddingsProvider") as string
|
const embeddingsProvider = formData.get("embeddingsProvider") as string
|
||||||
|
|
||||||
|
console.log("......[process] Starting file processing request,embeddingsProvider=", embeddingsProvider)
|
||||||
|
|
||||||
const { data: fileMetadata, error: metadataError } = await supabaseAdmin
|
const { data: fileMetadata, error: metadataError } = await supabaseAdmin
|
||||||
.from("files")
|
.from("files")
|
||||||
.select("*")
|
.select("*")
|
||||||
|
|
@ -87,7 +91,9 @@ export async function POST(req: Request) {
|
||||||
chunks = await processMarkdown(blob)
|
chunks = await processMarkdown(blob)
|
||||||
break
|
break
|
||||||
case "pdf":
|
case "pdf":
|
||||||
|
console.log("......[process] Processing PDF...")
|
||||||
chunks = await processPdf(blob)
|
chunks = await processPdf(blob)
|
||||||
|
console.log("......[process] PDF Processed chunks=", chunks)
|
||||||
break
|
break
|
||||||
case "txt":
|
case "txt":
|
||||||
chunks = await processTxt(blob)
|
chunks = await processTxt(blob)
|
||||||
|
|
@ -136,6 +142,42 @@ export async function POST(req: Request) {
|
||||||
})
|
})
|
||||||
|
|
||||||
embeddings = await Promise.all(embeddingPromises)
|
embeddings = await Promise.all(embeddingPromises)
|
||||||
|
} else if (embeddingsProvider === "bge-m3"){
|
||||||
|
|
||||||
|
// 示例:调用你自己的 BGE-M3 API 或本地函数
|
||||||
|
// const embeddingPromises = chunks.map(async chunk => {
|
||||||
|
const embeddingPromises = chunks.map(async (chunk, index) => {
|
||||||
|
try {
|
||||||
|
// return await generateBgeM3Embedding(chunk.content)
|
||||||
|
|
||||||
|
const result = await generateBgeM3Embedding(chunk.content)
|
||||||
|
|
||||||
|
if (!Array.isArray(result)) {
|
||||||
|
console.error(`......❌ Chunk ${index}: result is not an array`, result)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.length !== 1024) {
|
||||||
|
console.error(`......❌ Chunk ${index}: incorrect length: ${result.length}`)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.every(x => typeof x === "number")) {
|
||||||
|
console.error(`......❌ Chunk ${index}: contains non-numbers`, result)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
console.error(`Error generating BGE-M3 embedding for chunk: ${chunk}`, error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
embeddings = await Promise.all(embeddingPromises)
|
||||||
|
console.log(`......[embedding] 维度: ${embeddings.length}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const file_items = chunks.map((chunk, index) => ({
|
const file_items = chunks.map((chunk, index) => ({
|
||||||
|
|
@ -150,6 +192,10 @@ export async function POST(req: Request) {
|
||||||
local_embedding:
|
local_embedding:
|
||||||
embeddingsProvider === "local"
|
embeddingsProvider === "local"
|
||||||
? ((embeddings[index] || null) as any)
|
? ((embeddings[index] || null) as any)
|
||||||
|
: null,
|
||||||
|
bge_m3_embedding:
|
||||||
|
embeddingsProvider === "bge-m3" && embeddings[index] && embeddings[index].length === 1024
|
||||||
|
? (embeddings[index] || null) as any
|
||||||
: null
|
: null
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
@ -162,6 +208,7 @@ export async function POST(req: Request) {
|
||||||
.update({ tokens: totalTokens })
|
.update({ tokens: totalTokens })
|
||||||
.eq("id", file_id)
|
.eq("id", file_id)
|
||||||
|
|
||||||
|
console.log("......[embedding]Embed Successful.")
|
||||||
return new NextResponse("Embed Successful", {
|
return new NextResponse("Embed Successful", {
|
||||||
status: 200
|
status: 200
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { generateLocalEmbedding } from "@/lib/generate-local-embedding"
|
import { generateLocalEmbedding } from "@/lib/generate-local-embedding"
|
||||||
|
import { generateBgeM3Embedding } from "@/lib/generate-bgem3-embedding" // 新增
|
||||||
import { checkApiKey, getServerProfile } from "@/lib/server/server-chat-helpers"
|
import { checkApiKey, getServerProfile } from "@/lib/server/server-chat-helpers"
|
||||||
import { Database } from "@/supabase/types"
|
import { Database } from "@/supabase/types"
|
||||||
import { createClient } from "@supabase/supabase-js"
|
import { createClient } from "@supabase/supabase-js"
|
||||||
|
|
@ -6,19 +7,28 @@ import OpenAI from "openai"
|
||||||
import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入
|
import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
export async function POST(request: Request) {
|
||||||
|
|
||||||
|
console.log("......[retrieve] request=", request)
|
||||||
|
|
||||||
const json = await request.json()
|
const json = await request.json()
|
||||||
const { userInput, fileIds, embeddingsProvider, sourceCount } = json as {
|
const { userInput, fileIds, embeddingsProvider, sourceCount } = json as {
|
||||||
userInput: string
|
userInput: string
|
||||||
fileIds: string[]
|
fileIds: string[]
|
||||||
embeddingsProvider: "openai" | "local"
|
embeddingsProvider: "openai" | "local" | "bge-m3"
|
||||||
sourceCount: number
|
sourceCount: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const uniqueFileIds = [...new Set(fileIds)]
|
const uniqueFileIds = [...new Set(fileIds)]
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
const rawSupaUrl = getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000"
|
||||||
|
const supaUrlObj = new URL(rawSupaUrl)
|
||||||
|
supaUrlObj.port = "8000"
|
||||||
|
|
||||||
const supabaseAdmin = createClient<Database>(
|
const supabaseAdmin = createClient<Database>(
|
||||||
getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000",
|
supaUrlObj.origin,
|
||||||
|
// getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000",
|
||||||
process.env.SUPABASE_SERVICE_ROLE_KEY!
|
process.env.SUPABASE_SERVICE_ROLE_KEY!
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -84,6 +94,37 @@ export async function POST(request: Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
chunks = localFileItems
|
chunks = localFileItems
|
||||||
|
} else if (embeddingsProvider === "bge-m3"){
|
||||||
|
// 示例:调用你自己的 BGE-M3 API 或本地函数
|
||||||
|
// 新增:使用 BGE-M3 嵌入
|
||||||
|
console.log("......[retrieve] userInput=",userInput)
|
||||||
|
const bgeEmbedding = await generateBgeM3Embedding(userInput)
|
||||||
|
//console.log("......[retrieve] [bge-m3] got embedding:", bgeEmbedding)
|
||||||
|
|
||||||
|
// 3. 调用 RPC 之前打印参数
|
||||||
|
console.log(
|
||||||
|
"......[retrieve] [bge-m3] calling RPC match_file_items_bge_m3 with:",
|
||||||
|
{
|
||||||
|
match_count: sourceCount,
|
||||||
|
file_ids: uniqueFileIds,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 调用对应的 RPC,需要在数据库侧提前定义 match_file_items_bge_m3
|
||||||
|
const { data: bgeFileItems, error: bgeError } =
|
||||||
|
await supabaseAdmin.rpc("match_file_items_bge_m3", {
|
||||||
|
query_embedding: bgeEmbedding as any,
|
||||||
|
match_count: sourceCount,
|
||||||
|
file_ids: uniqueFileIds
|
||||||
|
})
|
||||||
|
if (bgeError) {
|
||||||
|
console.log("......[retrieve] [bge-m3] RPC error full:", bgeError)
|
||||||
|
console.log("......[retrieve] [bge-m3] error.message:", bgeError.message)
|
||||||
|
throw bgeError
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("......[retrieve] [bge-m3] RPC result count =", bgeFileItems?.length)
|
||||||
|
chunks = bgeFileItems
|
||||||
}
|
}
|
||||||
|
|
||||||
const mostSimilarChunks = chunks?.sort(
|
const mostSimilarChunks = chunks?.sort(
|
||||||
|
|
@ -94,6 +135,12 @@ export async function POST(request: Request) {
|
||||||
status: 200
|
status: 200
|
||||||
})
|
})
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
||||||
|
// 把完整错误对象都打印出来
|
||||||
|
console.log("......[retrieve] Caught error:", error)
|
||||||
|
console.log("......[retrieve] error.message:", error.message)
|
||||||
|
console.log("......[retrieve] error.error:", error.error)
|
||||||
|
|
||||||
const errorMessage = error.error?.message || "An unexpected error occurred"
|
const errorMessage = error.error?.message || "An unexpected error occurred"
|
||||||
const errorCode = error.status || 500
|
const errorCode = error.status || 500
|
||||||
return new Response(JSON.stringify({ message: errorMessage }), {
|
return new Response(JSON.stringify({ message: errorMessage }), {
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,12 @@ import { Button } from "../ui/button"
|
||||||
import { FilePreview } from "../ui/file-preview"
|
import { FilePreview } from "../ui/file-preview"
|
||||||
import { WithTooltip } from "../ui/with-tooltip"
|
import { WithTooltip } from "../ui/with-tooltip"
|
||||||
import { ChatRetrievalSettings } from "./chat-retrieval-settings"
|
import { ChatRetrievalSettings } from "./chat-retrieval-settings"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
interface ChatFilesDisplayProps {}
|
interface ChatFilesDisplayProps {}
|
||||||
|
|
||||||
export const ChatFilesDisplay: FC<ChatFilesDisplayProps> = ({}) => {
|
export const ChatFilesDisplay: FC<ChatFilesDisplayProps> = ({}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
useHotkey("f", () => setShowFilesDisplay(prev => !prev))
|
useHotkey("f", () => setShowFilesDisplay(prev => !prev))
|
||||||
useHotkey("e", () => setUseRetrieval(prev => !prev))
|
useHotkey("e", () => setUseRetrieval(prev => !prev))
|
||||||
|
|
||||||
|
|
@ -106,7 +108,7 @@ export const ChatFilesDisplay: FC<ChatFilesDisplayProps> = ({}) => {
|
||||||
>
|
>
|
||||||
<RetrievalToggle />
|
<RetrievalToggle />
|
||||||
|
|
||||||
<div>Hide files</div>
|
<div>{t("RAG.hideFiles")}</div>
|
||||||
|
|
||||||
<div onClick={e => e.stopPropagation()}>
|
<div onClick={e => e.stopPropagation()}>
|
||||||
<ChatRetrievalSettings />
|
<ChatRetrievalSettings />
|
||||||
|
|
@ -233,10 +235,14 @@ export const ChatFilesDisplay: FC<ChatFilesDisplayProps> = ({}) => {
|
||||||
>
|
>
|
||||||
<RetrievalToggle />
|
<RetrievalToggle />
|
||||||
|
|
||||||
<div>
|
{/* <div>
|
||||||
{" "}
|
{" "}
|
||||||
View {combinedMessageFiles.length} file
|
View {combinedMessageFiles.length} file
|
||||||
{combinedMessageFiles.length > 1 ? "s" : ""}
|
{combinedMessageFiles.length > 1 ? "s" : ""}
|
||||||
|
</div> */}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{t("RAG.viewFiles", { count: combinedMessageFiles.length })}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div onClick={e => e.stopPropagation()}>
|
<div onClick={e => e.stopPropagation()}>
|
||||||
|
|
@ -249,6 +255,7 @@ export const ChatFilesDisplay: FC<ChatFilesDisplayProps> = ({}) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const RetrievalToggle = ({}) => {
|
const RetrievalToggle = ({}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
const { useRetrieval, setUseRetrieval } = useContext(ChatbotUIContext)
|
const { useRetrieval, setUseRetrieval } = useContext(ChatbotUIContext)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -259,8 +266,8 @@ const RetrievalToggle = ({}) => {
|
||||||
display={
|
display={
|
||||||
<div>
|
<div>
|
||||||
{useRetrieval
|
{useRetrieval
|
||||||
? "File retrieval is enabled on the selected files for this message. Click the indicator to disable."
|
? t("RAG.retrievalEnabled")
|
||||||
: "Click the indicator to enable file retrieval for this message."}
|
: t("RAG.retrievalDisabled")}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
trigger={
|
trigger={
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,13 @@ import {
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
import { v4 as uuidv4 } from "uuid"
|
import { v4 as uuidv4 } from "uuid"
|
||||||
|
import { getRuntimeEnv } from "@/lib/ipconfig"
|
||||||
|
|
||||||
|
|
||||||
|
type RetrievedFileItem = Tables<"file_items"> & {
|
||||||
|
similarity: number
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export const validateChatSettings = (
|
export const validateChatSettings = (
|
||||||
chatSettings: ChatSettings | null,
|
chatSettings: ChatSettings | null,
|
||||||
|
|
@ -55,7 +62,7 @@ export const handleRetrieval = async (
|
||||||
userInput: string,
|
userInput: string,
|
||||||
newMessageFiles: ChatFile[],
|
newMessageFiles: ChatFile[],
|
||||||
chatFiles: ChatFile[],
|
chatFiles: ChatFile[],
|
||||||
embeddingsProvider: "openai" | "local",
|
embeddingsProvider: "openai" | "local" | "bge-m3",
|
||||||
sourceCount: number
|
sourceCount: number
|
||||||
) => {
|
) => {
|
||||||
const response = await fetch("/api/retrieval/retrieve", {
|
const response = await fetch("/api/retrieval/retrieve", {
|
||||||
|
|
@ -73,10 +80,86 @@ export const handleRetrieval = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
const { results } = (await response.json()) as {
|
const { results } = (await response.json()) as {
|
||||||
results: Tables<"file_items">[]
|
//results: Tables<"file_items">[]
|
||||||
|
results: RetrievedFileItem[]
|
||||||
}
|
}
|
||||||
|
|
||||||
return results
|
// return results
|
||||||
|
|
||||||
|
// ✅ 打印全部相似度得分和对应内容
|
||||||
|
results.forEach((item, index) => {
|
||||||
|
console.log(`Result ${index + 1}: similarity = ${(item.similarity * 100).toFixed(2)}%, content = ${item.content.slice(0, 100)}...`)
|
||||||
|
})
|
||||||
|
|
||||||
|
// ✅ 只保留 similarity >= 0.8 的记录(大于等于 80%)
|
||||||
|
// const filteredResults = results.filter(item => item.similarity >= 0.8)
|
||||||
|
|
||||||
|
// return filteredResults
|
||||||
|
|
||||||
|
// // 阈值设定
|
||||||
|
// const HIGH_THRESHOLD = 0.8
|
||||||
|
// const LOW_THRESHOLD = 0.5
|
||||||
|
|
||||||
|
// let filteredResults: RetrievedFileItem[] = []
|
||||||
|
|
||||||
|
// // 找出所有 ≥ 0.8 的结果
|
||||||
|
// const highSimilarity = results.filter(item => item.similarity >= HIGH_THRESHOLD)
|
||||||
|
|
||||||
|
// if (highSimilarity.length > 0) {
|
||||||
|
// // ✅ 高相似度:返回全部
|
||||||
|
// filteredResults = highSimilarity
|
||||||
|
// } else {
|
||||||
|
// // ❌ 无高相似度
|
||||||
|
// const acceptable = results.filter(item => item.similarity >= LOW_THRESHOLD)
|
||||||
|
|
||||||
|
// if (acceptable.length === 0) {
|
||||||
|
// // 🚫 无结果满足最低要求
|
||||||
|
// filteredResults = []
|
||||||
|
// } else {
|
||||||
|
// // 🔍 返回最佳项
|
||||||
|
// const best = acceptable.reduce((a, b) => (a.similarity > b.similarity ? a : b))
|
||||||
|
// filteredResults = [best]
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// console.log("🔍 筛选后结果:", {
|
||||||
|
// total: results.length,
|
||||||
|
// ">=80": highSimilarity.length,
|
||||||
|
// ">=50": results.filter(r => r.similarity >= LOW_THRESHOLD).length,
|
||||||
|
// finalReturned: filteredResults.length,
|
||||||
|
// topMatchScore: filteredResults[0]?.similarity
|
||||||
|
// })
|
||||||
|
|
||||||
|
// return filteredResults
|
||||||
|
|
||||||
|
|
||||||
|
// 阈值设定
|
||||||
|
const HIGH_THRESHOLD = 0.8
|
||||||
|
const LOW_THRESHOLD = 0.51
|
||||||
|
|
||||||
|
let filteredResults: RetrievedFileItem[] = []
|
||||||
|
|
||||||
|
// 收集所有 ≥ 0.51 的结果(中等和高)
|
||||||
|
const acceptable = results.filter(item => item.similarity >= LOW_THRESHOLD)
|
||||||
|
|
||||||
|
if (acceptable.length === 0) {
|
||||||
|
// 🚫 全部太低,直接返回空
|
||||||
|
filteredResults = []
|
||||||
|
} else {
|
||||||
|
// ✅ 返回所有 ≥ 0.51 的
|
||||||
|
filteredResults = acceptable
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("🔍 筛选后结果:", {
|
||||||
|
total: results.length,
|
||||||
|
">=80": results.filter(r => r.similarity >= HIGH_THRESHOLD).length,
|
||||||
|
">=50": acceptable.length,
|
||||||
|
finalReturned: filteredResults.length,
|
||||||
|
topMatchScore: filteredResults[0]?.similarity
|
||||||
|
})
|
||||||
|
|
||||||
|
return filteredResults
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createTempMessages = (
|
export const createTempMessages = (
|
||||||
|
|
|
||||||
|
|
@ -120,6 +120,7 @@ export const useChatHandler = () => {
|
||||||
embeddingsProvider: selectedAssistant.embeddings_provider as
|
embeddingsProvider: selectedAssistant.embeddings_provider as
|
||||||
| "openai"
|
| "openai"
|
||||||
| "local"
|
| "local"
|
||||||
|
| "bge-m3"
|
||||||
})
|
})
|
||||||
|
|
||||||
let allFiles = []
|
let allFiles = []
|
||||||
|
|
@ -261,6 +262,17 @@ export const useChatHandler = () => {
|
||||||
|
|
||||||
let retrievedFileItems: Tables<"file_items">[] = []
|
let retrievedFileItems: Tables<"file_items">[] = []
|
||||||
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
'......[handleSendMessage] newMessageFiles.length =',
|
||||||
|
newMessageFiles.length,
|
||||||
|
', chatFiles.length =',
|
||||||
|
chatFiles.length,
|
||||||
|
', useRetrieval =',
|
||||||
|
useRetrieval
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(newMessageFiles.length > 0 || chatFiles.length > 0) &&
|
(newMessageFiles.length > 0 || chatFiles.length > 0) &&
|
||||||
useRetrieval
|
useRetrieval
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,103 @@
|
||||||
|
// import { useChatHandler } from "@/components/chat/chat-hooks/use-chat-handler"
|
||||||
|
// import { ChatbotUIContext } from "@/context/context"
|
||||||
|
// import { Tables } from "@/supabase/types"
|
||||||
|
// import { FC, useContext, useState } from "react"
|
||||||
|
// import { Message } from "../messages/message"
|
||||||
|
|
||||||
|
// interface ChatMessagesProps {}
|
||||||
|
|
||||||
|
// export const ChatMessages: FC<ChatMessagesProps> = ({}) => {
|
||||||
|
// const { chatMessages, chatFileItems } = useContext(ChatbotUIContext)
|
||||||
|
|
||||||
|
// const { handleSendEdit } = useChatHandler()
|
||||||
|
|
||||||
|
// const [editingMessage, setEditingMessage] = useState<Tables<"messages">>()
|
||||||
|
|
||||||
|
// return chatMessages
|
||||||
|
// .sort((a, b) => a.message.sequence_number - b.message.sequence_number)
|
||||||
|
// .map((chatMessage, index, array) => {
|
||||||
|
// const messageFileItems = chatFileItems.filter(
|
||||||
|
// (chatFileItem, _, self) =>
|
||||||
|
// chatMessage.fileItems.includes(chatFileItem.id) &&
|
||||||
|
// self.findIndex(item => item.id === chatFileItem.id) === _
|
||||||
|
// )
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <Message
|
||||||
|
// key={chatMessage.message.sequence_number}
|
||||||
|
// message={chatMessage.message}
|
||||||
|
// fileItems={messageFileItems}
|
||||||
|
// isEditing={editingMessage?.id === chatMessage.message.id}
|
||||||
|
// isLast={index === array.length - 1}
|
||||||
|
// onStartEdit={setEditingMessage}
|
||||||
|
// onCancelEdit={() => setEditingMessage(undefined)}
|
||||||
|
// onSubmitEdit={handleSendEdit}
|
||||||
|
// />
|
||||||
|
// )
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import { useChatHandler } from "@/components/chat/chat-hooks/use-chat-handler"
|
import { useChatHandler } from "@/components/chat/chat-hooks/use-chat-handler"
|
||||||
import { ChatbotUIContext } from "@/context/context"
|
import { ChatbotUIContext } from "@/context/context"
|
||||||
import { Tables } from "@/supabase/types"
|
import { Tables } from "@/supabase/types"
|
||||||
import { FC, useContext, useState } from "react"
|
import { FC, useContext, useState, useRef, useEffect } from "react"
|
||||||
import { Message } from "../messages/message"
|
import { Message } from "../messages/message"
|
||||||
|
|
||||||
interface ChatMessagesProps {}
|
interface ChatMessagesProps {}
|
||||||
|
|
||||||
export const ChatMessages: FC<ChatMessagesProps> = ({}) => {
|
export const ChatMessages: FC<ChatMessagesProps> = ({}) => {
|
||||||
const { chatMessages, chatFileItems } = useContext(ChatbotUIContext)
|
const { chatMessages, chatFileItems } = useContext(ChatbotUIContext)
|
||||||
|
|
||||||
const { handleSendEdit } = useChatHandler()
|
const { handleSendEdit } = useChatHandler()
|
||||||
|
|
||||||
const [editingMessage, setEditingMessage] = useState<Tables<"messages">>()
|
const [editingMessage, setEditingMessage] = useState<Tables<"messages">>()
|
||||||
|
|
||||||
return chatMessages
|
// 1. 创建一个 ref 指向滚动容器
|
||||||
.sort((a, b) => a.message.sequence_number - b.message.sequence_number)
|
const containerRef = useRef<HTMLDivElement>(null)
|
||||||
.map((chatMessage, index, array) => {
|
|
||||||
const messageFileItems = chatFileItems.filter(
|
|
||||||
(chatFileItem, _, self) =>
|
|
||||||
chatMessage.fileItems.includes(chatFileItem.id) &&
|
|
||||||
self.findIndex(item => item.id === chatFileItem.id) === _
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
// 2. 每次 chatMessages 变化时,滚到底部
|
||||||
<Message
|
useEffect(() => {
|
||||||
key={chatMessage.message.sequence_number}
|
const dom = containerRef.current
|
||||||
message={chatMessage.message}
|
if (dom) {
|
||||||
fileItems={messageFileItems}
|
dom.scrollTo({ top: dom.scrollHeight, behavior: "smooth" })
|
||||||
isEditing={editingMessage?.id === chatMessage.message.id}
|
}
|
||||||
isLast={index === array.length - 1}
|
}, [chatMessages])
|
||||||
onStartEdit={setEditingMessage}
|
|
||||||
onCancelEdit={() => setEditingMessage(undefined)}
|
// 先排序
|
||||||
onSubmitEdit={handleSendEdit}
|
const sorted = [...chatMessages].sort(
|
||||||
/>
|
(a, b) => a.message.sequence_number - b.message.sequence_number
|
||||||
)
|
)
|
||||||
})
|
|
||||||
|
return (
|
||||||
|
// 3. 给外层 div 加上 overflow-auto,并且要有高度限制
|
||||||
|
<div
|
||||||
|
ref={containerRef}
|
||||||
|
className="w-full h-screen overflow-auto"
|
||||||
|
style={{ maxHeight: "100vh" }}
|
||||||
|
>
|
||||||
|
{sorted.map((chatMessage, index, array) => {
|
||||||
|
const messageFileItems = chatFileItems.filter(
|
||||||
|
(chatFileItem, _, self) =>
|
||||||
|
chatMessage.fileItems.includes(chatFileItem.id) &&
|
||||||
|
self.findIndex(item => item.id === chatFileItem.id) === _
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Message
|
||||||
|
key={chatMessage.message.sequence_number}
|
||||||
|
message={chatMessage.message}
|
||||||
|
fileItems={messageFileItems}
|
||||||
|
isEditing={editingMessage?.id === chatMessage.message.id}
|
||||||
|
isLast={index === array.length - 1}
|
||||||
|
onStartEdit={setEditingMessage}
|
||||||
|
onCancelEdit={() => setEditingMessage(undefined)}
|
||||||
|
onSubmitEdit={handleSendEdit}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,14 @@ import {
|
||||||
import { Label } from "../ui/label"
|
import { Label } from "../ui/label"
|
||||||
import { Slider } from "../ui/slider"
|
import { Slider } from "../ui/slider"
|
||||||
import { WithTooltip } from "../ui/with-tooltip"
|
import { WithTooltip } from "../ui/with-tooltip"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
interface ChatRetrievalSettingsProps {}
|
interface ChatRetrievalSettingsProps {}
|
||||||
|
|
||||||
export const ChatRetrievalSettings: FC<ChatRetrievalSettingsProps> = ({}) => {
|
export const ChatRetrievalSettings: FC<ChatRetrievalSettingsProps> = ({}) => {
|
||||||
|
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const { sourceCount, setSourceCount } = useContext(ChatbotUIContext)
|
const { sourceCount, setSourceCount } = useContext(ChatbotUIContext)
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
|
|
@ -25,7 +29,7 @@ export const ChatRetrievalSettings: FC<ChatRetrievalSettingsProps> = ({}) => {
|
||||||
<WithTooltip
|
<WithTooltip
|
||||||
delayDuration={0}
|
delayDuration={0}
|
||||||
side="top"
|
side="top"
|
||||||
display={<div>Adjust retrieval settings.</div>}
|
display={<div>{t("RAG.adjustRetrievalSettings")}.</div>}
|
||||||
trigger={
|
trigger={
|
||||||
<IconAdjustmentsHorizontal
|
<IconAdjustmentsHorizontal
|
||||||
className="cursor-pointer pt-[4px] hover:opacity-50"
|
className="cursor-pointer pt-[4px] hover:opacity-50"
|
||||||
|
|
@ -38,7 +42,7 @@ export const ChatRetrievalSettings: FC<ChatRetrievalSettingsProps> = ({}) => {
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<Label className="flex items-center space-x-1">
|
<Label className="flex items-center space-x-1">
|
||||||
<div>Source Count:</div>
|
<div>{t("RAG.sourceCount")}:</div>
|
||||||
|
|
||||||
<div>{sourceCount}</div>
|
<div>{sourceCount}</div>
|
||||||
</Label>
|
</Label>
|
||||||
|
|
@ -56,7 +60,7 @@ export const ChatRetrievalSettings: FC<ChatRetrievalSettingsProps> = ({}) => {
|
||||||
|
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button size="sm" onClick={() => setIsOpen(false)}>
|
<Button size="sm" onClick={() => setIsOpen(false)}>
|
||||||
Save & Close
|
{t("RAG.saveAndClose")}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ import { TextareaAutosize } from "../ui/textarea-autosize"
|
||||||
import { WithTooltip } from "../ui/with-tooltip"
|
import { WithTooltip } from "../ui/with-tooltip"
|
||||||
import { MessageActions } from "./message-actions"
|
import { MessageActions } from "./message-actions"
|
||||||
import { MessageMarkdown } from "./message-markdown"
|
import { MessageMarkdown } from "./message-markdown"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
|
|
||||||
const ICON_SIZE = 32
|
const ICON_SIZE = 32
|
||||||
|
|
||||||
|
|
@ -36,6 +38,100 @@ interface MessageProps {
|
||||||
onSubmitEdit: (value: string, sequenceNumber: number) => void
|
onSubmitEdit: (value: string, sequenceNumber: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ✅ 提取为组件,放在同文件中也没问题
|
||||||
|
// const MessageContent: React.FC<{ content: string }> = ({ content }) => {
|
||||||
|
// const [showThink, setShowThink] = useState(false)
|
||||||
|
|
||||||
|
// let thinkContent = ""
|
||||||
|
// let visibleContent = content.trim()
|
||||||
|
|
||||||
|
// const fullMatch = content.match(/<think>([\s\S]*?)<\/think>/i)
|
||||||
|
// if (fullMatch) {
|
||||||
|
// thinkContent = fullMatch[1].trim()
|
||||||
|
// visibleContent = content.replace(fullMatch[0], "").trim()
|
||||||
|
// } else {
|
||||||
|
// const endOnlyMatch = content.match(/([\s\S]*?)<\/think>/i)
|
||||||
|
// if (endOnlyMatch) {
|
||||||
|
// thinkContent = endOnlyMatch[1].trim()
|
||||||
|
// visibleContent = content.replace(endOnlyMatch[0], "").trim()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <div className="space-y-2">
|
||||||
|
// <MessageMarkdown content={visibleContent} />
|
||||||
|
|
||||||
|
// {thinkContent && (
|
||||||
|
// <div className="mt-2 text-sm text-muted-foreground">
|
||||||
|
// <button
|
||||||
|
// className="text-blue-500 hover:underline"
|
||||||
|
// onClick={() => setShowThink(prev => !prev)}
|
||||||
|
// >
|
||||||
|
// {showThink ? "隐藏思考过程 ▲" : "显示思考过程 ▼"}
|
||||||
|
// </button>
|
||||||
|
|
||||||
|
// {showThink && (
|
||||||
|
// <div className="mt-2 rounded bg-gray-100 p-3 text-sm dark:bg-gray-800">
|
||||||
|
// <MessageMarkdown content={thinkContent} />
|
||||||
|
// </div>
|
||||||
|
// )}
|
||||||
|
// </div>
|
||||||
|
// )}
|
||||||
|
// </div>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
const MessageContent: React.FC<{ content: string }> = ({ content }) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const [showThink, setShowThink] = useState(false)
|
||||||
|
|
||||||
|
let thinkContent = ""
|
||||||
|
let visibleContent = content.trim()
|
||||||
|
|
||||||
|
const fullMatch = content.match(/<think>([\s\S]*?)<\/think>/i)
|
||||||
|
if (fullMatch) {
|
||||||
|
thinkContent = fullMatch[1].trim()
|
||||||
|
visibleContent = content.replace(fullMatch[0], "").trim()
|
||||||
|
} else {
|
||||||
|
const endOnlyMatch = content.match(/([\s\S]*?)<\/think>/i)
|
||||||
|
if (endOnlyMatch) {
|
||||||
|
thinkContent = endOnlyMatch[1].trim()
|
||||||
|
visibleContent = content.replace(endOnlyMatch[0], "").trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-2">
|
||||||
|
{/* 折叠内容提前显示 */}
|
||||||
|
{thinkContent && (
|
||||||
|
<div className="text-sm text-muted-foreground">
|
||||||
|
<button
|
||||||
|
className="text-sm font-medium text-foreground/70 hover:text-foreground underline underline-offset-2"
|
||||||
|
onClick={() => setShowThink(prev => !prev)}
|
||||||
|
>
|
||||||
|
|
||||||
|
{showThink
|
||||||
|
? t("chat.hideThinking") + " ▲"
|
||||||
|
: t("chat.showThinking") + " ▼"}
|
||||||
|
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{showThink && (
|
||||||
|
<div className="mt-2 rounded bg-gray-100 p-3 text-sm dark:bg-gray-800">
|
||||||
|
<MessageMarkdown content={thinkContent} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 主消息显示在折叠内容之后 */}
|
||||||
|
<MessageMarkdown content={visibleContent} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export const Message: FC<MessageProps> = ({
|
export const Message: FC<MessageProps> = ({
|
||||||
message,
|
message,
|
||||||
fileItems,
|
fileItems,
|
||||||
|
|
@ -62,7 +158,11 @@ export const Message: FC<MessageProps> = ({
|
||||||
models
|
models
|
||||||
} = useContext(ChatbotUIContext)
|
} = useContext(ChatbotUIContext)
|
||||||
|
|
||||||
const { handleSendMessage } = useChatHandler()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const [showThink, setShowThink] = useState(false) //添加Think的状态
|
||||||
|
|
||||||
|
const { handleSendMessage } = useChatHandler()
|
||||||
|
|
||||||
const editInputRef = useRef<HTMLTextAreaElement>(null)
|
const editInputRef = useRef<HTMLTextAreaElement>(null)
|
||||||
|
|
||||||
|
|
@ -209,7 +309,7 @@ export const Message: FC<MessageProps> = ({
|
||||||
size={ICON_SIZE}
|
size={ICON_SIZE}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="text-lg font-semibold">Prompt</div>
|
<div className="text-lg font-semibold">{t("RAG.prompt")}</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
|
|
@ -282,7 +382,7 @@ export const Message: FC<MessageProps> = ({
|
||||||
<div className="flex animate-pulse items-center space-x-2">
|
<div className="flex animate-pulse items-center space-x-2">
|
||||||
<IconFileText size={20} />
|
<IconFileText size={20} />
|
||||||
|
|
||||||
<div>Searching files...</div>
|
<div>{t("RAG.searchingFiles")}...</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
default:
|
default:
|
||||||
|
|
@ -290,7 +390,8 @@ export const Message: FC<MessageProps> = ({
|
||||||
<div className="flex animate-pulse items-center space-x-2">
|
<div className="flex animate-pulse items-center space-x-2">
|
||||||
<IconBolt size={20} />
|
<IconBolt size={20} />
|
||||||
|
|
||||||
<div>Using {toolInUse}...</div>
|
{/* <div>Using {toolInUse}...</div> */}
|
||||||
|
<div>{t("RAG.usingTool", { tool: toolInUse })}</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -305,7 +406,19 @@ export const Message: FC<MessageProps> = ({
|
||||||
maxRows={20}
|
maxRows={20}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<MessageMarkdown content={message.content} />
|
|
||||||
|
|
||||||
|
|
||||||
|
// <MessageMarkdown content={message.content} />
|
||||||
|
|
||||||
|
|
||||||
|
<MessageContent content={message.content} />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -407,11 +520,11 @@ export const Message: FC<MessageProps> = ({
|
||||||
{isEditing && (
|
{isEditing && (
|
||||||
<div className="mt-4 flex justify-center space-x-2">
|
<div className="mt-4 flex justify-center space-x-2">
|
||||||
<Button size="sm" onClick={handleSendEdit}>
|
<Button size="sm" onClick={handleSendEdit}>
|
||||||
Save & Send
|
{t("RAG.saveAndSend")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button size="sm" variant="outline" onClick={onCancelEdit}>
|
<Button size="sm" variant="outline" onClick={onCancelEdit}>
|
||||||
Cancel
|
{t("RAG.cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,9 @@ export const ModelSelect: FC<ModelSelectProps> = ({
|
||||||
...availableOpenRouterModels
|
...availableOpenRouterModels
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// console.log("......ModelSelect - selectedModelId:", selectedModelId)
|
||||||
|
// console.log("......ModelSelect - allModels IDs:", allModels.map(m => m.modelId))
|
||||||
|
|
||||||
const groupedModels = allModels.reduce<Record<string, LLM[]>>(
|
const groupedModels = allModels.reduce<Record<string, LLM[]>>(
|
||||||
(groups, model) => {
|
(groups, model) => {
|
||||||
const key = model.provider
|
const key = model.provider
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ interface StepContainerProps {
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
showBackButton?: boolean
|
showBackButton?: boolean
|
||||||
showNextButton?: boolean
|
showNextButton?: boolean
|
||||||
|
isSubmitting?: boolean // ✅ 新增这个
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StepContainer: FC<StepContainerProps> = ({
|
export const StepContainer: FC<StepContainerProps> = ({
|
||||||
|
|
@ -30,7 +31,8 @@ export const StepContainer: FC<StepContainerProps> = ({
|
||||||
onShouldProceed,
|
onShouldProceed,
|
||||||
children,
|
children,
|
||||||
showBackButton = false,
|
showBackButton = false,
|
||||||
showNextButton = true
|
showNextButton = true,
|
||||||
|
isSubmitting = false // ✅ 加上这行
|
||||||
}) => {
|
}) => {
|
||||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||||
|
|
||||||
|
|
@ -80,6 +82,7 @@ export const StepContainer: FC<StepContainerProps> = ({
|
||||||
ref={buttonRef}
|
ref={buttonRef}
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => onShouldProceed(true)}
|
onClick={() => onShouldProceed(true)}
|
||||||
|
disabled={isSubmitting}
|
||||||
>
|
>
|
||||||
{t("setup.next")}
|
{t("setup.next")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ export const SidebarCreateItem: FC<SidebarCreateItemProps> = ({
|
||||||
file,
|
file,
|
||||||
rest,
|
rest,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
selectedWorkspace.embeddings_provider as "openai" | "local"
|
selectedWorkspace.embeddings_provider as "openai" | "local" | "bge-m3"
|
||||||
)
|
)
|
||||||
|
|
||||||
return createdFile
|
return createdFile
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ export const CreateFile: FC<CreateFileProps> = ({ isOpen, onOpenChange }) => {
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
placeholder={t("side.fileDescriptionPlaceholder")}
|
placeholder={t("side.fileDescriptionPlaceholder")}
|
||||||
value={name}
|
value={description} // ✅ 正确
|
||||||
onChange={e => setDescription(e.target.value)}
|
onChange={e => setDescription(e.target.value)}
|
||||||
maxLength={FILE_DESCRIPTION_MAX}
|
maxLength={FILE_DESCRIPTION_MAX}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -234,7 +234,7 @@ const AdvancedContent: FC<AdvancedContentProps> = ({
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
value={chatSettings.embeddingsProvider}
|
value={chatSettings.embeddingsProvider}
|
||||||
onValueChange={(embeddingsProvider: "openai" | "local") => {
|
onValueChange={(embeddingsProvider: "openai" | "local" | "bge-m3") => {
|
||||||
onChangeChatSettings({
|
onChangeChatSettings({
|
||||||
...chatSettings,
|
...chatSettings,
|
||||||
embeddingsProvider
|
embeddingsProvider
|
||||||
|
|
@ -253,6 +253,9 @@ const AdvancedContent: FC<AdvancedContentProps> = ({
|
||||||
{window.location.hostname === "localhost" && (
|
{window.location.hostname === "localhost" && (
|
||||||
<SelectItem value="local">{t("chat.local")}</SelectItem>
|
<SelectItem value="local">{t("chat.local")}</SelectItem>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<SelectItem value="bge-m3">BGE-M3</SelectItem>
|
||||||
|
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -96,13 +96,13 @@ export const GlobalState: FC<GlobalStateProps> = ({ children }) => {
|
||||||
const [userInput, setUserInput] = useState<string>("")
|
const [userInput, setUserInput] = useState<string>("")
|
||||||
const [chatMessages, setChatMessages] = useState<ChatMessage[]>([])
|
const [chatMessages, setChatMessages] = useState<ChatMessage[]>([])
|
||||||
const [chatSettings, setChatSettings] = useState<ChatSettings>({
|
const [chatSettings, setChatSettings] = useState<ChatSettings>({
|
||||||
model: "gpt-4-turbo-preview",
|
model: "GPT",
|
||||||
prompt: "You are a helpful AI assistant.",
|
prompt: "You are a helpful AI assistant.",
|
||||||
temperature: 0.5,
|
temperature: 0.5,
|
||||||
contextLength: 4000,
|
contextLength: 4000,
|
||||||
includeProfileContext: true,
|
includeProfileContext: true,
|
||||||
includeWorkspaceInstructions: true,
|
includeWorkspaceInstructions: true,
|
||||||
embeddingsProvider: "openai"
|
embeddingsProvider: "bge-m3"
|
||||||
})
|
})
|
||||||
const [selectedChat, setSelectedChat] = useState<Tables<"chats"> | null>(null)
|
const [selectedChat, setSelectedChat] = useState<Tables<"chats"> | null>(null)
|
||||||
const [chatFileItems, setChatFileItems] = useState<Tables<"file_items">[]>([])
|
const [chatFileItems, setChatFileItems] = useState<Tables<"file_items">[]>([])
|
||||||
|
|
@ -181,12 +181,8 @@ export const GlobalState: FC<GlobalStateProps> = ({ children }) => {
|
||||||
setProfile(profile)
|
setProfile(profile)
|
||||||
|
|
||||||
if (profile !== null && profile !== undefined && !profile.has_onboarded) {
|
if (profile !== null && profile !== undefined && !profile.has_onboarded) {
|
||||||
// 修正 homePath 拼接逻辑,避免 //setup 问题
|
|
||||||
const homePath = locale === defaultLocale ? "" : `/${locale}`
|
const homePath = locale === defaultLocale ? "" : `/${locale}`
|
||||||
//const targetPath = `${homePath}/setup`
|
|
||||||
const targetPath = `${homePath}/setup`.replace(/\/\//g, '/')
|
const targetPath = `${homePath}/setup`.replace(/\/\//g, '/')
|
||||||
|
|
||||||
|
|
||||||
router.push(targetPath)
|
router.push(targetPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -143,6 +143,7 @@ export const WorkspaceSettings: FC<WorkspaceSettingsProps> = ({}) => {
|
||||||
embeddingsProvider: defaultChatSettings.embeddingsProvider as
|
embeddingsProvider: defaultChatSettings.embeddingsProvider as
|
||||||
| "openai"
|
| "openai"
|
||||||
| "local"
|
| "local"
|
||||||
|
| "bge-m3"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ export const createFileBasedOnExtension = async (
|
||||||
file: File,
|
file: File,
|
||||||
fileRecord: TablesInsert<"files">,
|
fileRecord: TablesInsert<"files">,
|
||||||
workspace_id: string,
|
workspace_id: string,
|
||||||
embeddingsProvider: "openai" | "local"
|
embeddingsProvider: "openai" | "local" | "bge-m3"
|
||||||
) => {
|
) => {
|
||||||
const fileExtension = file.name.split(".").pop()
|
const fileExtension = file.name.split(".").pop()
|
||||||
|
|
||||||
|
|
@ -89,9 +89,11 @@ export const createFile = async (
|
||||||
file: File,
|
file: File,
|
||||||
fileRecord: TablesInsert<"files">,
|
fileRecord: TablesInsert<"files">,
|
||||||
workspace_id: string,
|
workspace_id: string,
|
||||||
embeddingsProvider: "openai" | "local"
|
embeddingsProvider: "openai" | "local" | "bge-m3"
|
||||||
) => {
|
) => {
|
||||||
let validFilename = fileRecord.name.replace(/[^a-z0-9.]/gi, "_").toLowerCase()
|
|
||||||
|
//let validFilename = fileRecord.name.replace(/[^a-z0-9.]/gi, "_").toLowerCase()
|
||||||
|
let validFilename = fileRecord.name.replace(/[<>:"/\\|?*%#\s]/g, "_")
|
||||||
const extension = file.name.split(".").pop()
|
const extension = file.name.split(".").pop()
|
||||||
const extensionIndex = validFilename.lastIndexOf(".")
|
const extensionIndex = validFilename.lastIndexOf(".")
|
||||||
const baseName = validFilename.substring(0, (extensionIndex < 0) ? undefined : extensionIndex)
|
const baseName = validFilename.substring(0, (extensionIndex < 0) ? undefined : extensionIndex)
|
||||||
|
|
@ -101,6 +103,7 @@ export const createFile = async (
|
||||||
} else {
|
} else {
|
||||||
fileRecord.name = baseName + "." + extension
|
fileRecord.name = baseName + "." + extension
|
||||||
}
|
}
|
||||||
|
console.log("......[createFile] Inserting file record:", fileRecord);
|
||||||
const { data: createdFile, error } = await supabase
|
const { data: createdFile, error } = await supabase
|
||||||
.from("files")
|
.from("files")
|
||||||
.insert([fileRecord])
|
.insert([fileRecord])
|
||||||
|
|
@ -116,7 +119,7 @@ export const createFile = async (
|
||||||
file_id: createdFile.id,
|
file_id: createdFile.id,
|
||||||
workspace_id
|
workspace_id
|
||||||
})
|
})
|
||||||
|
console.log("......[createFile] Uploading file to storage:", createdFile.name);
|
||||||
const filePath = await uploadFile(file, {
|
const filePath = await uploadFile(file, {
|
||||||
name: createdFile.name,
|
name: createdFile.name,
|
||||||
user_id: createdFile.user_id,
|
user_id: createdFile.user_id,
|
||||||
|
|
@ -131,11 +134,20 @@ export const createFile = async (
|
||||||
formData.append("file_id", createdFile.id)
|
formData.append("file_id", createdFile.id)
|
||||||
formData.append("embeddingsProvider", embeddingsProvider)
|
formData.append("embeddingsProvider", embeddingsProvider)
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`......[createFile] Sending process request for file ${createdFile.id}, provider: ${embeddingsProvider}`
|
||||||
|
);
|
||||||
|
|
||||||
const response = await fetch("/api/retrieval/process", {
|
const response = await fetch("/api/retrieval/process", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: formData
|
body: formData
|
||||||
})
|
})
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`......[createFile] /api/retrieval/process responded status=${response.status}`
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const jsonText = await response.text()
|
const jsonText = await response.text()
|
||||||
const json = JSON.parse(jsonText)
|
const json = JSON.parse(jsonText)
|
||||||
|
|
@ -159,8 +171,21 @@ export const createDocXFile = async (
|
||||||
file: File,
|
file: File,
|
||||||
fileRecord: TablesInsert<"files">,
|
fileRecord: TablesInsert<"files">,
|
||||||
workspace_id: string,
|
workspace_id: string,
|
||||||
embeddingsProvider: "openai" | "local"
|
embeddingsProvider: "openai" | "local" | "bge-m3"
|
||||||
) => {
|
) => {
|
||||||
|
|
||||||
|
let validFilename = fileRecord.name.replace(/[<>:"/\\|?*%#\s]/g, "_")
|
||||||
|
const extension = file.name.split(".").pop()
|
||||||
|
const extensionIndex = validFilename.lastIndexOf(".")
|
||||||
|
const baseName = validFilename.substring(0, (extensionIndex < 0) ? undefined : extensionIndex)
|
||||||
|
const maxBaseNameLength = 100 - (extension?.length || 0) - 1
|
||||||
|
if (baseName.length > maxBaseNameLength) {
|
||||||
|
fileRecord.name = baseName.substring(0, maxBaseNameLength) + "." + extension
|
||||||
|
} else {
|
||||||
|
fileRecord.name = baseName + "." + extension
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const { data: createdFile, error } = await supabase
|
const { data: createdFile, error } = await supabase
|
||||||
.from("files")
|
.from("files")
|
||||||
.insert([fileRecord])
|
.insert([fileRecord])
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,12 @@ export const CHAT_SETTING_LIMITS: Record<LLMID, ChatSettingLimits> = {
|
||||||
MAX_TOKEN_OUTPUT_LENGTH: 4096,
|
MAX_TOKEN_OUTPUT_LENGTH: 4096,
|
||||||
MAX_CONTEXT_LENGTH: 128000
|
MAX_CONTEXT_LENGTH: 128000
|
||||||
},
|
},
|
||||||
|
"GPT": {
|
||||||
|
MIN_TEMPERATURE: 0.0,
|
||||||
|
MAX_TEMPERATURE: 2.0,
|
||||||
|
MAX_TOKEN_OUTPUT_LENGTH: 4096,
|
||||||
|
MAX_CONTEXT_LENGTH: 8192
|
||||||
|
},
|
||||||
|
|
||||||
// PERPLEXITY MODELS
|
// PERPLEXITY MODELS
|
||||||
"pplx-7b-online": {
|
"pplx-7b-online": {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { getRuntimeEnv } from "@/lib/ipconfig"
|
||||||
|
|
||||||
|
export async function generateBgeM3Embedding(text: string): Promise<number[] | null> {
|
||||||
|
try {
|
||||||
|
// 取 Supabase URL 或本地默认
|
||||||
|
const supaUrl = getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000"
|
||||||
|
// 构造 Embedding 服务地址:同 host + 8001 端口
|
||||||
|
const urlObj = new URL(supaUrl)
|
||||||
|
urlObj.port = "8001" // 强制改成 8001
|
||||||
|
const apiUrl = `${urlObj.origin}/v1/embeddings`
|
||||||
|
console.debug("......[generateBgeM3Embedding] apiUrl =", apiUrl)
|
||||||
|
|
||||||
|
const response = await fetch(apiUrl, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({
|
||||||
|
// OpenAI 兼容请求字段
|
||||||
|
input: text,
|
||||||
|
model: "text-embedding-bge-m3"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to fetch BGE-M3 embedding: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回结构为 { object, data: [{ embedding, … }], model, usage }
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
// 取 data[0].embedding
|
||||||
|
if (Array.isArray(result.data) && result.data.length > 0) {
|
||||||
|
return result.data[0].embedding as number[];
|
||||||
|
} else {
|
||||||
|
console.error("Unexpected embedding response format:", result);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error in generateBgeM3Embedding:", err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,39 +6,6 @@
|
||||||
// return process.env[key];
|
// return process.env[key];
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// let _env: Record<string, string> | null = null
|
|
||||||
// export function getRuntimeEnv(key: string): string | undefined {
|
|
||||||
// console.log("============>>Getting Supabase API URL.")
|
|
||||||
|
|
||||||
// if (typeof window !== "undefined") {
|
|
||||||
// if (!_env && typeof window.RUNTIME_ENV !== "undefined") {
|
|
||||||
// _env = window.RUNTIME_ENV
|
|
||||||
// console.log("[browser-side] Retrieved API endpoint from window.RUNTIME_ENV:", _env);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (_env) {
|
|
||||||
// console.log("[browser-side]Returning cached API from _env:", _env)
|
|
||||||
// return _env[key]
|
|
||||||
// }
|
|
||||||
|
|
||||||
// console.log("[browser-side]No window.RUNTIME_ENV found in browser")
|
|
||||||
// return undefined
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // 服务端始终动态读取 process.env
|
|
||||||
// const val = process.env[key]
|
|
||||||
// console.log("[server-side] Falling back to process.env for key:", key, "value:", val)
|
|
||||||
// return val
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let _env: Record<string, string> | null = null
|
let _env: Record<string, string> | null = null
|
||||||
|
|
||||||
// 最大重试次数
|
// 最大重试次数
|
||||||
|
|
@ -47,7 +14,7 @@ const MAX_RETRIES = 5
|
||||||
const RETRY_DELAY = 1000
|
const RETRY_DELAY = 1000
|
||||||
|
|
||||||
export function getRuntimeEnv(key: string): string | undefined {
|
export function getRuntimeEnv(key: string): string | undefined {
|
||||||
console.log("============>>Getting Supabase API URL.")
|
//console.log("============>>Getting Supabase API URL.")
|
||||||
|
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
if (!_env && typeof window.RUNTIME_ENV !== "undefined") {
|
if (!_env && typeof window.RUNTIME_ENV !== "undefined") {
|
||||||
|
|
@ -88,6 +55,6 @@ export function getRuntimeEnv(key: string): string | undefined {
|
||||||
|
|
||||||
// 服务端始终动态读取 process.env
|
// 服务端始终动态读取 process.env
|
||||||
const val = process.env[key]
|
const val = process.env[key]
|
||||||
console.log("[server-side] Falling back to process.env for key:", key, "value:", val)
|
//console.log("[server-side] Falling back to process.env for key:", key, "value:", val)
|
||||||
return val // 服务端直接返回 process.env 的值
|
return val // 服务端直接返回 process.env 的值
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,19 @@ export const fetchOllamaModels = async () => {
|
||||||
process.env.NEXT_PUBLIC_OLLAMA_URL + "/api/tags"
|
process.env.NEXT_PUBLIC_OLLAMA_URL + "/api/tags"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// const response = await fetch(
|
||||||
|
// process.env.NEXT_PUBLIC_OLLAMA_URL + "/api/tags",
|
||||||
|
// {
|
||||||
|
// method: "GET",
|
||||||
|
// headers: {
|
||||||
|
// "Authorization": "Bearer token-abc123",
|
||||||
|
// "Content-Type": "application/json"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Ollama server is not responding.`)
|
throw new Error(`LLM server is not responding.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
|
|
|
||||||
|
|
@ -7,19 +7,6 @@ import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入
|
||||||
|
|
||||||
export async function getServerProfile() {
|
export async function getServerProfile() {
|
||||||
|
|
||||||
// const cookieStore = cookies()
|
|
||||||
// const supabase = createServerClient<Database>(
|
|
||||||
// getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000",
|
|
||||||
// process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
||||||
// {
|
|
||||||
// cookies: {
|
|
||||||
// get(name: string) {
|
|
||||||
// return cookieStore.get(name)?.value
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// )
|
|
||||||
|
|
||||||
const supabase = getSupabaseServerClient()
|
const supabase = getSupabaseServerClient()
|
||||||
|
|
||||||
const user = (await supabase.auth.getUser()).data.user
|
const user = (await supabase.auth.getUser()).data.user
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,10 @@ module.exports = withBundleAnalyzer(
|
||||||
protocol: "http",
|
protocol: "http",
|
||||||
hostname: "127.0.0.1"
|
hostname: "127.0.0.1"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
protocol: "http",
|
||||||
|
hostname: "**"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
protocol: "https",
|
protocol: "https",
|
||||||
hostname: "**"
|
hostname: "**"
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
"back": "رجوع",
|
"back": "رجوع",
|
||||||
"next": "التالي",
|
"next": "التالي",
|
||||||
"WelcomeToChatDeskUI": "مرحبًا بك في ChatDesk، منصة الواجهة الأمامية لتطوير أبحاث دردشة الذكاء الاصطناعي",
|
"WelcomeToChatDeskUI": "مرحبًا بك في ChatDesk، منصة الواجهة الأمامية لتطوير أبحاث دردشة الذكاء الاصطناعي",
|
||||||
"ClickNextToStartChatting": "انقر على 'التالي' لبدء الدردشة مع نموذج اللغة الكبير باستخدام ChatDesk."
|
"ClickNextToStartChatting": "انقر على 'التالي' لبدء الدردشة مع نموذج اللغة الكبير باستخدام ChatDesk.",
|
||||||
|
"Default": "افتراضي",
|
||||||
|
"DefaultLLMModelCreatedDuringOnboarding": "تم إنشاء نموذج LLM الافتراضي أثناء الإعداد"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"email": "البريد الإلكتروني",
|
"email": "البريد الإلكتروني",
|
||||||
|
|
@ -61,7 +63,8 @@
|
||||||
"signupNotAllowed": "البريد الإلكتروني {{email}} غير مسموح له بالتسجيل.",
|
"signupNotAllowed": "البريد الإلكتروني {{email}} غير مسموح له بالتسجيل.",
|
||||||
"unexpectedError": "حدث خطأ غير متوقع",
|
"unexpectedError": "حدث خطأ غير متوقع",
|
||||||
"invalidCredentials": "البريد الإلكتروني أو كلمة المرور غير صحيحة.",
|
"invalidCredentials": "البريد الإلكتروني أو كلمة المرور غير صحيحة.",
|
||||||
"clearing": "جارٍ مسح حالة تسجيل الدخول..."
|
"clearing": "جارٍ مسح حالة تسجيل الدخول...",
|
||||||
|
"sessionExpired": "انتهت صلاحية الجلسة الخاصة بك. يرجى التسجيل أو تسجيل الدخول مرة أخرى."
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"defaultChatTitle": "الدردشة",
|
"defaultChatTitle": "الدردشة",
|
||||||
|
|
@ -85,7 +88,9 @@
|
||||||
"selectModel": "اختر نموذجًا",
|
"selectModel": "اختر نموذجًا",
|
||||||
"hosted": "مستضاف",
|
"hosted": "مستضاف",
|
||||||
"advancedSettings": "الإعدادات المتقدمة",
|
"advancedSettings": "الإعدادات المتقدمة",
|
||||||
"searchModelsPlaceholder": "ابحث عن النماذج..."
|
"searchModelsPlaceholder": "ابحث عن النماذج...",
|
||||||
|
"showThinking": "عرض عملية التفكير",
|
||||||
|
"hideThinking": "إخفاء عملية التفكير"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"settingsTitle": "إعدادات المستخدم",
|
"settingsTitle": "إعدادات المستخدم",
|
||||||
|
|
@ -283,5 +288,19 @@
|
||||||
"enabled": "مفعل",
|
"enabled": "مفعل",
|
||||||
"disabled": "معطل",
|
"disabled": "معطل",
|
||||||
"startNewChat": "ابدأ محادثة جديدة"
|
"startNewChat": "ابدأ محادثة جديدة"
|
||||||
|
},
|
||||||
|
"RAG": {
|
||||||
|
"prompt": "الموجه",
|
||||||
|
"searchingFiles": "جارٍ البحث في الملفات...",
|
||||||
|
"usingTool": "يتم استخدام {{tool}}...",
|
||||||
|
"saveAndSend": "حفظ وإرسال",
|
||||||
|
"cancel": "إلغاء",
|
||||||
|
"hideFiles": "إخفاء الملفات",
|
||||||
|
"retrievalEnabled": "تم تمكين استرجاع الملفات على الملفات المحددة لهذه الرسالة. انقر على المؤشر للتعطيل.",
|
||||||
|
"retrievalDisabled": "انقر على المؤشر لتمكين استرجاع الملفات لهذه الرسالة.",
|
||||||
|
"viewFiles": "عرض {{count}} {{count, plural, one {ملف} other {ملفات}}}",
|
||||||
|
"sourceCount": "عدد المصادر:",
|
||||||
|
"adjustRetrievalSettings": "ضبط إعدادات الاسترجاع.",
|
||||||
|
"saveAndClose": "حفظ وإغلاق"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
"back": "পেছনে",
|
"back": "পেছনে",
|
||||||
"next": "পরবর্তী",
|
"next": "পরবর্তী",
|
||||||
"WelcomeToChatDeskUI": "ChatDesk-এ স্বাগতম, এটি একটি ফ্রন্টএন্ড প্ল্যাটফর্ম AI চ্যাট গবেষণা ও উন্নয়নের জন্য।",
|
"WelcomeToChatDeskUI": "ChatDesk-এ স্বাগতম, এটি একটি ফ্রন্টএন্ড প্ল্যাটফর্ম AI চ্যাট গবেষণা ও উন্নয়নের জন্য।",
|
||||||
"ClickNextToStartChatting": "'পরবর্তী' ক্লিক করে আপনার LLM-এর সাথে ChatDesk ব্যবহার করে চ্যাটিং শুরু করুন।"
|
"ClickNextToStartChatting": "'পরবর্তী' ক্লিক করে আপনার LLM-এর সাথে ChatDesk ব্যবহার করে চ্যাটিং শুরু করুন।",
|
||||||
|
"Default": "ডিফল্ট",
|
||||||
|
"DefaultLLMModelCreatedDuringOnboarding": "অনবোর্ডিং চলাকালীন ডিফল্ট LLM মডেল তৈরি করা হয়েছে"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"email": "ইমেইল",
|
"email": "ইমেইল",
|
||||||
|
|
@ -61,7 +63,8 @@
|
||||||
"signupNotAllowed": "ইমেইল {{email}} দিয়ে সাইন আপ করা অনুমোদিত নয়।",
|
"signupNotAllowed": "ইমেইল {{email}} দিয়ে সাইন আপ করা অনুমোদিত নয়।",
|
||||||
"unexpectedError": "একটি অপ্রত্যাশিত ত্রুটি ঘটেছে",
|
"unexpectedError": "একটি অপ্রত্যাশিত ত্রুটি ঘটেছে",
|
||||||
"invalidCredentials": "অবৈধ ইমেইল বা পাসওয়ার্ড।",
|
"invalidCredentials": "অবৈধ ইমেইল বা পাসওয়ার্ড।",
|
||||||
"clearing": "লগইন অবস্থা মুছে ফেলা হচ্ছে..."
|
"clearing": "লগইন অবস্থা মুছে ফেলা হচ্ছে...",
|
||||||
|
"sessionExpired": "আপনার সেশনটির মেয়াদ শেষ হয়েছে। অনুগ্রহ করে আবার সাইন আপ বা লগইন করুন।"
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"defaultChatTitle": "চ্যাট",
|
"defaultChatTitle": "চ্যাট",
|
||||||
|
|
@ -85,7 +88,9 @@
|
||||||
"selectModel": "একটি মডেল নির্বাচন করুন",
|
"selectModel": "একটি মডেল নির্বাচন করুন",
|
||||||
"hosted": "হোস্টেড",
|
"hosted": "হোস্টেড",
|
||||||
"advancedSettings": "উন্নত সেটিংস",
|
"advancedSettings": "উন্নত সেটিংস",
|
||||||
"searchModelsPlaceholder": "মডেল খুঁজুন..."
|
"searchModelsPlaceholder": "মডেল খুঁজুন...",
|
||||||
|
"showThinking": "ভাবনার প্রক্রিয়া দেখান",
|
||||||
|
"hideThinking": "ভাবনার প্রক্রিয়া লুকান"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"settingsTitle": "ব্যবহারকারী সেটিংস",
|
"settingsTitle": "ব্যবহারকারী সেটিংস",
|
||||||
|
|
@ -283,5 +288,19 @@
|
||||||
"enabled": "সক্রিয়",
|
"enabled": "সক্রিয়",
|
||||||
"disabled": "নিষ্ক্রিয়",
|
"disabled": "নিষ্ক্রিয়",
|
||||||
"startNewChat": "নতুন চ্যাট শুরু করুন"
|
"startNewChat": "নতুন চ্যাট শুরু করুন"
|
||||||
|
},
|
||||||
|
"RAG": {
|
||||||
|
"prompt": "প্রম্পট",
|
||||||
|
"searchingFiles": "ফাইল অনুসন্ধান করা হচ্ছে...",
|
||||||
|
"usingTool": "{{tool}} ব্যবহার করা হচ্ছে...",
|
||||||
|
"saveAndSend": "সংরক্ষণ এবং প্রেরণ",
|
||||||
|
"cancel": "বাতিল করুন",
|
||||||
|
"hideFiles": "ফাইল লুকান",
|
||||||
|
"retrievalEnabled": "এই বার্তার জন্য নির্বাচিত ফাইলগুলিতে ফাইল পুনরুদ্ধার সক্রিয় করা হয়েছে। নিষ্ক্রিয় করতে সূচকে ক্লিক করুন।",
|
||||||
|
"retrievalDisabled": "এই বার্তার জন্য ফাইল পুনরুদ্ধার সক্রিয় করতে সূচকে ক্লিক করুন।",
|
||||||
|
"viewFiles": "{{count}} {{count, plural, one {ফাইল} other {ফাইলগুলি}}} দেখান",
|
||||||
|
"sourceCount": "উৎসের সংখ্যা:",
|
||||||
|
"adjustRetrievalSettings": "পুনরুদ্ধার সেটিংস সমন্বয় করুন।",
|
||||||
|
"saveAndClose": "সংরক্ষণ করুন এবং বন্ধ করুন"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
"back": "Zurück",
|
"back": "Zurück",
|
||||||
"next": "Weiter",
|
"next": "Weiter",
|
||||||
"WelcomeToChatDeskUI": "Willkommen bei ChatDesk, der Front-End-Plattform für KI-Chat-Forschung und -Entwicklung",
|
"WelcomeToChatDeskUI": "Willkommen bei ChatDesk, der Front-End-Plattform für KI-Chat-Forschung und -Entwicklung",
|
||||||
"ClickNextToStartChatting": "Klicke auf 'Weiter', um mit deinem LLM über ChatDesk zu chatten."
|
"ClickNextToStartChatting": "Klicke auf 'Weiter', um mit deinem LLM über ChatDesk zu chatten.",
|
||||||
|
"Default": "Standard",
|
||||||
|
"DefaultLLMModelCreatedDuringOnboarding": "Standard-LLM-Modell während der Einrichtung erstellt"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"email": "E-Mail",
|
"email": "E-Mail",
|
||||||
|
|
@ -61,7 +63,8 @@
|
||||||
"signupNotAllowed": "Die E-Mail-Adresse {{email}} ist nicht zur Registrierung zugelassen.",
|
"signupNotAllowed": "Die E-Mail-Adresse {{email}} ist nicht zur Registrierung zugelassen.",
|
||||||
"unexpectedError": "Ein unerwarteter Fehler ist aufgetreten.",
|
"unexpectedError": "Ein unerwarteter Fehler ist aufgetreten.",
|
||||||
"invalidCredentials": "Ungültige E-Mail-Adresse oder ungültiges Passwort.",
|
"invalidCredentials": "Ungültige E-Mail-Adresse oder ungültiges Passwort.",
|
||||||
"clearing": "Anmeldestatus wird gelöscht..."
|
"clearing": "Anmeldestatus wird gelöscht...",
|
||||||
|
"sessionExpired": "Ihre Sitzung ist abgelaufen. Bitte registrieren Sie sich erneut oder melden Sie sich erneut an."
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"defaultChatTitle": "Chat",
|
"defaultChatTitle": "Chat",
|
||||||
|
|
@ -85,7 +88,9 @@
|
||||||
"selectModel": "Modell auswählen",
|
"selectModel": "Modell auswählen",
|
||||||
"hosted": "Gehostet",
|
"hosted": "Gehostet",
|
||||||
"advancedSettings": "Erweiterte Einstellungen",
|
"advancedSettings": "Erweiterte Einstellungen",
|
||||||
"searchModelsPlaceholder": "Modelle suchen..."
|
"searchModelsPlaceholder": "Modelle suchen...",
|
||||||
|
"showThinking": "Denkprozess anzeigen",
|
||||||
|
"hideThinking": "Denkprozess ausblenden"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"settingsTitle": "Benutzereinstellungen",
|
"settingsTitle": "Benutzereinstellungen",
|
||||||
|
|
@ -283,5 +288,19 @@
|
||||||
"enabled": "Aktiviert",
|
"enabled": "Aktiviert",
|
||||||
"disabled": "Deaktiviert",
|
"disabled": "Deaktiviert",
|
||||||
"startNewChat": "Neuen Chat starten"
|
"startNewChat": "Neuen Chat starten"
|
||||||
|
},
|
||||||
|
"RAG": {
|
||||||
|
"prompt": "Prompt",
|
||||||
|
"searchingFiles": "Dateien werden durchsucht...",
|
||||||
|
"usingTool": "{{tool}} wird verwendet...",
|
||||||
|
"saveAndSend": "Speichern & Senden",
|
||||||
|
"cancel": "Abbrechen",
|
||||||
|
"hideFiles": "Dateien ausblenden",
|
||||||
|
"retrievalEnabled": "Die Dateiwiederherstellung ist für die ausgewählten Dateien dieser Nachricht aktiviert. Klicken Sie auf den Indikator, um sie zu deaktivieren.",
|
||||||
|
"retrievalDisabled": "Klicken Sie auf den Indikator, um die Dateiwiederherstellung für diese Nachricht zu aktivieren.",
|
||||||
|
"viewFiles": "{{count}} {{count, plural, one {Datei} other {Dateien}}} anzeigen",
|
||||||
|
"sourceCount": "Quellenanzahl:",
|
||||||
|
"adjustRetrievalSettings": "Abrufeinstellungen anpassen.",
|
||||||
|
"saveAndClose": "Speichern & Schließen"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
"back": "Back",
|
"back": "Back",
|
||||||
"next": "Next",
|
"next": "Next",
|
||||||
"WelcomeToChatDeskUI": "Welcome to ChatDesk, the Front-End Platform for AI Chat R&D",
|
"WelcomeToChatDeskUI": "Welcome to ChatDesk, the Front-End Platform for AI Chat R&D",
|
||||||
"ClickNextToStartChatting": "Click 'Next' to start chatting with your LLM using ChatDesk."
|
"ClickNextToStartChatting": "Click 'Next' to start chatting with your LLM using ChatDesk.",
|
||||||
|
"Default": "Default",
|
||||||
|
"DefaultLLMModelCreatedDuringOnboarding": "Default LLM model created during onboarding"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
|
|
@ -61,7 +63,8 @@
|
||||||
"signupNotAllowed": "Email {{email}} is not allowed to sign up.",
|
"signupNotAllowed": "Email {{email}} is not allowed to sign up.",
|
||||||
"unexpectedError": "An unexpected error occurred",
|
"unexpectedError": "An unexpected error occurred",
|
||||||
"invalidCredentials": "Invalid email or password.",
|
"invalidCredentials": "Invalid email or password.",
|
||||||
"clearing": "Clearing login state..."
|
"clearing": "Clearing login state...",
|
||||||
|
"sessionExpired": "Your session has expired. Please sign up or log in again."
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"defaultChatTitle": "Chat",
|
"defaultChatTitle": "Chat",
|
||||||
|
|
@ -85,7 +88,9 @@
|
||||||
"selectModel": "Select a model",
|
"selectModel": "Select a model",
|
||||||
"hosted": "Hosted",
|
"hosted": "Hosted",
|
||||||
"advancedSettings": "Advanced Settings",
|
"advancedSettings": "Advanced Settings",
|
||||||
"searchModelsPlaceholder": "Search models..."
|
"searchModelsPlaceholder": "Search models...",
|
||||||
|
"showThinking": "Show Thought Process",
|
||||||
|
"hideThinking": "Hide Thought Process"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"settingsTitle": "User Settings",
|
"settingsTitle": "User Settings",
|
||||||
|
|
@ -283,5 +288,19 @@
|
||||||
"enabled": "Enabled",
|
"enabled": "Enabled",
|
||||||
"disabled": "Disabled",
|
"disabled": "Disabled",
|
||||||
"startNewChat": "Start a new chat"
|
"startNewChat": "Start a new chat"
|
||||||
|
},
|
||||||
|
"RAG": {
|
||||||
|
"prompt": "Prompt",
|
||||||
|
"searchingFiles": "Searching files...",
|
||||||
|
"usingTool": "Using {{tool}}...",
|
||||||
|
"saveAndSend": "Save & Send",
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"hideFiles": "Hide files",
|
||||||
|
"retrievalEnabled": "File retrieval is enabled on the selected files for this message. Click the indicator to disable.",
|
||||||
|
"retrievalDisabled": "Click the indicator to enable file retrieval for this message.",
|
||||||
|
"viewFiles": "View {{count}} {{count, plural, one {file} other {files}}}",
|
||||||
|
"sourceCount": "Source Count:",
|
||||||
|
"adjustRetrievalSettings": "Adjust retrieval settings.",
|
||||||
|
"saveAndClose": "Save & Close"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
"back": "Atrás",
|
"back": "Atrás",
|
||||||
"next": "Siguiente",
|
"next": "Siguiente",
|
||||||
"WelcomeToChatDeskUI": "Bienvenido a ChatDesk, la plataforma frontend para el desarrollo de chats con IA",
|
"WelcomeToChatDeskUI": "Bienvenido a ChatDesk, la plataforma frontend para el desarrollo de chats con IA",
|
||||||
"ClickNextToStartChatting": "Haz clic en 'Siguiente' para comenzar a chatear con tu LLM usando ChatDesk."
|
"ClickNextToStartChatting": "Haz clic en 'Siguiente' para comenzar a chatear con tu LLM usando ChatDesk.",
|
||||||
|
"Default": "Predeterminado",
|
||||||
|
"DefaultLLMModelCreatedDuringOnboarding": "Modelo LLM predeterminado creado durante la incorporación"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"email": "Correo electrónico",
|
"email": "Correo electrónico",
|
||||||
|
|
@ -61,7 +63,8 @@
|
||||||
"signupNotAllowed": "El correo {{email}} no está permitido para registrarse.",
|
"signupNotAllowed": "El correo {{email}} no está permitido para registrarse.",
|
||||||
"unexpectedError": "Ocurrió un error inesperado.",
|
"unexpectedError": "Ocurrió un error inesperado.",
|
||||||
"invalidCredentials": "Correo electrónico o contraseña inválidos.",
|
"invalidCredentials": "Correo electrónico o contraseña inválidos.",
|
||||||
"clearing": "Eliminando el estado de inicio de sesión..."
|
"clearing": "Eliminando el estado de inicio de sesión...",
|
||||||
|
"sessionExpired": "Tu sesión ha expirado. Por favor, regístrate o inicia sesión nuevamente."
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"defaultChatTitle": "Chat",
|
"defaultChatTitle": "Chat",
|
||||||
|
|
@ -85,7 +88,9 @@
|
||||||
"selectModel": "Seleccionar modelo",
|
"selectModel": "Seleccionar modelo",
|
||||||
"hosted": "Alojado",
|
"hosted": "Alojado",
|
||||||
"advancedSettings": "Configuración avanzada",
|
"advancedSettings": "Configuración avanzada",
|
||||||
"searchModelsPlaceholder": "Buscar modelos..."
|
"searchModelsPlaceholder": "Buscar modelos...",
|
||||||
|
"showThinking": "Mostrar proceso de pensamiento",
|
||||||
|
"hideThinking": "Ocultar proceso de pensamiento"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"settingsTitle": "Configuración del usuario",
|
"settingsTitle": "Configuración del usuario",
|
||||||
|
|
@ -283,5 +288,19 @@
|
||||||
"enabled": "Activado",
|
"enabled": "Activado",
|
||||||
"disabled": "Desactivado",
|
"disabled": "Desactivado",
|
||||||
"startNewChat": "Iniciar nuevo chat"
|
"startNewChat": "Iniciar nuevo chat"
|
||||||
|
},
|
||||||
|
"RAG": {
|
||||||
|
"prompt": "Indicador",
|
||||||
|
"searchingFiles": "Buscando archivos...",
|
||||||
|
"usingTool": "Usando {{tool}}...",
|
||||||
|
"saveAndSend": "Guardar y enviar",
|
||||||
|
"cancel": "Cancelar",
|
||||||
|
"hideFiles": "Ocultar archivos",
|
||||||
|
"retrievalEnabled": "La recuperación de archivos está habilitada en los archivos seleccionados para este mensaje. Haz clic en el indicador para desactivar.",
|
||||||
|
"retrievalDisabled": "Haz clic en el indicador para habilitar la recuperación de archivos para este mensaje.",
|
||||||
|
"viewFiles": "Ver {{count}} {{count, plural, one {archivo} other {archivos}}}",
|
||||||
|
"sourceCount": "Cantidad de fuentes:",
|
||||||
|
"adjustRetrievalSettings": "Ajustar configuración de recuperación.",
|
||||||
|
"saveAndClose": "Guardar y cerrar"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
"back": "Retour",
|
"back": "Retour",
|
||||||
"next": "Suivant",
|
"next": "Suivant",
|
||||||
"WelcomeToChatDeskUI": "Bienvenue sur ChatDesk, la plateforme frontale pour la R&D en chat IA",
|
"WelcomeToChatDeskUI": "Bienvenue sur ChatDesk, la plateforme frontale pour la R&D en chat IA",
|
||||||
"ClickNextToStartChatting": "Cliquez sur 'Suivant' pour commencer à discuter avec votre LLM via ChatDesk."
|
"ClickNextToStartChatting": "Cliquez sur 'Suivant' pour commencer à discuter avec votre LLM via ChatDesk.",
|
||||||
|
"Default": "Par défaut",
|
||||||
|
"DefaultLLMModelCreatedDuringOnboarding": "Modèle LLM par défaut créé lors de l'intégration"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"email": "E-mail",
|
"email": "E-mail",
|
||||||
|
|
@ -61,7 +63,8 @@
|
||||||
"signupNotAllowed": "L’e-mail {{email}} n’est pas autorisé à s’inscrire.",
|
"signupNotAllowed": "L’e-mail {{email}} n’est pas autorisé à s’inscrire.",
|
||||||
"unexpectedError": "Une erreur inattendue s’est produite",
|
"unexpectedError": "Une erreur inattendue s’est produite",
|
||||||
"invalidCredentials": "E-mail ou mot de passe invalide.",
|
"invalidCredentials": "E-mail ou mot de passe invalide.",
|
||||||
"clearing": "Suppression de l'état de connexion..."
|
"clearing": "Suppression de l'état de connexion...",
|
||||||
|
"sessionExpired": "Votre session a expiré. Veuillez vous inscrire ou vous reconnecter."
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"defaultChatTitle": "Discussion",
|
"defaultChatTitle": "Discussion",
|
||||||
|
|
@ -85,7 +88,9 @@
|
||||||
"selectModel": "Sélectionner un modèle",
|
"selectModel": "Sélectionner un modèle",
|
||||||
"hosted": "Hébergé",
|
"hosted": "Hébergé",
|
||||||
"advancedSettings": "Paramètres avancés",
|
"advancedSettings": "Paramètres avancés",
|
||||||
"searchModelsPlaceholder": "Rechercher des modèles..."
|
"searchModelsPlaceholder": "Rechercher des modèles...",
|
||||||
|
"showThinking": "Afficher le processus de réflexion",
|
||||||
|
"hideThinking": "Masquer le processus de réflexion"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"settingsTitle": "Paramètres utilisateur",
|
"settingsTitle": "Paramètres utilisateur",
|
||||||
|
|
@ -283,5 +288,19 @@
|
||||||
"enabled": "Activé",
|
"enabled": "Activé",
|
||||||
"disabled": "Désactivé",
|
"disabled": "Désactivé",
|
||||||
"startNewChat": "Commencer une nouvelle discussion"
|
"startNewChat": "Commencer une nouvelle discussion"
|
||||||
|
},
|
||||||
|
"RAG": {
|
||||||
|
"prompt": "Invite",
|
||||||
|
"searchingFiles": "Recherche de fichiers...",
|
||||||
|
"usingTool": "Utilisation de {{tool}}...",
|
||||||
|
"saveAndSend": "Enregistrer et envoyer",
|
||||||
|
"cancel": "Annuler",
|
||||||
|
"hideFiles": "Masquer les fichiers",
|
||||||
|
"retrievalEnabled": "La récupération de fichiers est activée sur les fichiers sélectionnés pour ce message. Cliquez sur l'indicateur pour désactiver.",
|
||||||
|
"retrievalDisabled": "Cliquez sur l'indicateur pour activer la récupération de fichiers pour ce message.",
|
||||||
|
"viewFiles": "Voir {{count}} {{count, plural, one {fichier} other {fichiers}}}",
|
||||||
|
"sourceCount": "Nombre de sources :",
|
||||||
|
"adjustRetrievalSettings": "Ajuster les paramètres de récupération.",
|
||||||
|
"saveAndClose": "Enregistrer et fermer"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
"back": "חזור",
|
"back": "חזור",
|
||||||
"next": "הבא",
|
"next": "הבא",
|
||||||
"WelcomeToChatDeskUI": "ברוך הבא ל-ChatDesk, פלטפורמת ממשק קדמי למחקר ופיתוח של צ'אט בינה מלאכותית",
|
"WelcomeToChatDeskUI": "ברוך הבא ל-ChatDesk, פלטפורמת ממשק קדמי למחקר ופיתוח של צ'אט בינה מלאכותית",
|
||||||
"ClickNextToStartChatting": "לחץ על 'הבא' כדי להתחיל לשוחח עם מודל השפה שלך באמצעות ChatDesk."
|
"ClickNextToStartChatting": "לחץ על 'הבא' כדי להתחיל לשוחח עם מודל השפה שלך באמצעות ChatDesk.",
|
||||||
|
"Default": "ברירת מחדל",
|
||||||
|
"DefaultLLMModelCreatedDuringOnboarding": "מודל LLM ברירת מחדל שנוצר במהלך ההגדרה הראשונית"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"email": "אימייל",
|
"email": "אימייל",
|
||||||
|
|
@ -61,7 +63,8 @@
|
||||||
"signupNotAllowed": "האימייל {{email}} אינו מורשה להירשם.",
|
"signupNotAllowed": "האימייל {{email}} אינו מורשה להירשם.",
|
||||||
"unexpectedError": "אירעה שגיאה בלתי צפויה",
|
"unexpectedError": "אירעה שגיאה בלתי צפויה",
|
||||||
"invalidCredentials": "אימייל או סיסמה לא נכונים.",
|
"invalidCredentials": "אימייל או סיסמה לא נכונים.",
|
||||||
"clearing": "מנקה את מצב ההתחברות..."
|
"clearing": "מנקה את מצב ההתחברות...",
|
||||||
|
"sessionExpired": "ההפעלה שלך פגה. אנא הירשם או התחבר מחדש."
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"defaultChatTitle": "צ'אט",
|
"defaultChatTitle": "צ'אט",
|
||||||
|
|
@ -85,7 +88,10 @@
|
||||||
"selectModel": "בחר מודל",
|
"selectModel": "בחר מודל",
|
||||||
"hosted": "מארח",
|
"hosted": "מארח",
|
||||||
"advancedSettings": "הגדרות מתקדמות",
|
"advancedSettings": "הגדרות מתקדמות",
|
||||||
"searchModelsPlaceholder": "חפש מודלים..."
|
"searchModelsPlaceholder": "חפש מודלים...",
|
||||||
|
"showThinking": "הצג תהליך חשיבה",
|
||||||
|
"hideThinking": "הסתר תהליך חשיבה"
|
||||||
|
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"settingsTitle": "הגדרות משתמש",
|
"settingsTitle": "הגדרות משתמש",
|
||||||
|
|
@ -283,5 +289,19 @@
|
||||||
"enabled": "מופעל",
|
"enabled": "מופעל",
|
||||||
"disabled": "מנוטרל",
|
"disabled": "מנוטרל",
|
||||||
"startNewChat": "התחל צ׳אט חדש"
|
"startNewChat": "התחל צ׳אט חדש"
|
||||||
|
},
|
||||||
|
"RAG": {
|
||||||
|
"prompt": "הנחיה",
|
||||||
|
"searchingFiles": "מחפש קבצים...",
|
||||||
|
"usingTool": "משתמש ב־{{tool}}...",
|
||||||
|
"saveAndSend": "שמור ושלח",
|
||||||
|
"cancel": "ביטול",
|
||||||
|
"hideFiles": "הסתר קבצים",
|
||||||
|
"retrievalEnabled": "שליפת קבצים מופעלת לקבצים שנבחרו עבור הודעה זו. לחץ על הסמן כדי להשבית.",
|
||||||
|
"retrievalDisabled": "לחץ על הסמן כדי להפעיל שליפת קבצים להודעה זו.",
|
||||||
|
"viewFiles": "הצג {{count}} {{count, plural, one {קובץ} other {קבצים}}}",
|
||||||
|
"sourceCount": "כמות מקורות:",
|
||||||
|
"adjustRetrievalSettings": "התאם הגדרות שליפה.",
|
||||||
|
"saveAndClose": "שמור וסגור"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
"back": "Kembali",
|
"back": "Kembali",
|
||||||
"next": "Berikutnya",
|
"next": "Berikutnya",
|
||||||
"WelcomeToChatDeskUI": "Selamat datang di ChatDesk, Platform Front-End untuk R&D Chat AI",
|
"WelcomeToChatDeskUI": "Selamat datang di ChatDesk, Platform Front-End untuk R&D Chat AI",
|
||||||
"ClickNextToStartChatting": "Klik 'Berikutnya' untuk mulai mengobrol dengan LLM Anda menggunakan ChatDesk."
|
"ClickNextToStartChatting": "Klik 'Berikutnya' untuk mulai mengobrol dengan LLM Anda menggunakan ChatDesk.",
|
||||||
|
"Default": "Bawaan",
|
||||||
|
"DefaultLLMModelCreatedDuringOnboarding": "Model LLM default dibuat selama proses onboarding"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
|
|
@ -61,7 +63,8 @@
|
||||||
"signupNotAllowed": "Email {{email}} tidak diizinkan untuk mendaftar.",
|
"signupNotAllowed": "Email {{email}} tidak diizinkan untuk mendaftar.",
|
||||||
"unexpectedError": "Terjadi kesalahan yang tidak terduga",
|
"unexpectedError": "Terjadi kesalahan yang tidak terduga",
|
||||||
"invalidCredentials": "Email atau kata sandi salah.",
|
"invalidCredentials": "Email atau kata sandi salah.",
|
||||||
"clearing": "Menghapus status login..."
|
"clearing": "Menghapus status login...",
|
||||||
|
"sessionExpired": "Sesi Anda telah kedaluwarsa. Silakan daftar atau masuk kembali."
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"defaultChatTitle": "Obrolan",
|
"defaultChatTitle": "Obrolan",
|
||||||
|
|
@ -85,7 +88,9 @@
|
||||||
"selectModel": "Pilih model",
|
"selectModel": "Pilih model",
|
||||||
"hosted": "Hosted",
|
"hosted": "Hosted",
|
||||||
"advancedSettings": "Pengaturan Lanjutan",
|
"advancedSettings": "Pengaturan Lanjutan",
|
||||||
"searchModelsPlaceholder": "Cari model..."
|
"searchModelsPlaceholder": "Cari model...",
|
||||||
|
"showThinking": "Tampilkan proses berpikir",
|
||||||
|
"hideThinking": "Sembunyikan proses berpikir"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"settingsTitle": "Pengaturan Pengguna",
|
"settingsTitle": "Pengaturan Pengguna",
|
||||||
|
|
@ -283,5 +288,19 @@
|
||||||
"enabled": "Aktif",
|
"enabled": "Aktif",
|
||||||
"disabled": "Nonaktif",
|
"disabled": "Nonaktif",
|
||||||
"startNewChat": "Mulai Obrolan Baru"
|
"startNewChat": "Mulai Obrolan Baru"
|
||||||
|
},
|
||||||
|
"RAG": {
|
||||||
|
"prompt": "Prompt",
|
||||||
|
"searchingFiles": "Mencari file...",
|
||||||
|
"usingTool": "Menggunakan {{tool}}...",
|
||||||
|
"saveAndSend": "Simpan & Kirim",
|
||||||
|
"cancel": "Batal",
|
||||||
|
"hideFiles": "Sembunyikan file",
|
||||||
|
"retrievalEnabled": "Pengambilan file diaktifkan pada file yang dipilih untuk pesan ini. Klik indikator untuk menonaktifkan.",
|
||||||
|
"retrievalDisabled": "Klik indikator untuk mengaktifkan pengambilan file untuk pesan ini.",
|
||||||
|
"viewFiles": "Lihat {{count}} berkas",
|
||||||
|
"sourceCount": "Jumlah Sumber:",
|
||||||
|
"adjustRetrievalSettings": "Sesuaikan pengaturan pengambilan.",
|
||||||
|
"saveAndClose": "Simpan & Tutup"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
"back": "Indietro",
|
"back": "Indietro",
|
||||||
"next": "Avanti",
|
"next": "Avanti",
|
||||||
"WelcomeToChatDeskUI": "Benvenuto in ChatDesk, la piattaforma frontend per la ricerca e sviluppo delle chat basate su AI",
|
"WelcomeToChatDeskUI": "Benvenuto in ChatDesk, la piattaforma frontend per la ricerca e sviluppo delle chat basate su AI",
|
||||||
"ClickNextToStartChatting": "Fai clic su 'Avanti' per iniziare a chattare con il tuo LLM utilizzando ChatDesk."
|
"ClickNextToStartChatting": "Fai clic su 'Avanti' per iniziare a chattare con il tuo LLM utilizzando ChatDesk.",
|
||||||
|
"Default": "Predefinito",
|
||||||
|
"DefaultLLMModelCreatedDuringOnboarding": "Modello LLM predefinito creato durante la configurazione"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
|
|
@ -61,7 +63,8 @@
|
||||||
"signupNotAllowed": "L'indirizzo email {{email}} non è autorizzato a registrarsi.",
|
"signupNotAllowed": "L'indirizzo email {{email}} non è autorizzato a registrarsi.",
|
||||||
"unexpectedError": "Si è verificato un errore imprevisto",
|
"unexpectedError": "Si è verificato un errore imprevisto",
|
||||||
"invalidCredentials": "Email o password non validi.",
|
"invalidCredentials": "Email o password non validi.",
|
||||||
"clearing": "Pulizia dello stato di accesso..."
|
"clearing": "Pulizia dello stato di accesso...",
|
||||||
|
"sessionExpired": "La tua sessione è scaduta. Registrati o accedi di nuovo."
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"defaultChatTitle": "Chat",
|
"defaultChatTitle": "Chat",
|
||||||
|
|
@ -85,7 +88,9 @@
|
||||||
"selectModel": "Seleziona un modello",
|
"selectModel": "Seleziona un modello",
|
||||||
"hosted": "Ospitato",
|
"hosted": "Ospitato",
|
||||||
"advancedSettings": "Impostazioni avanzate",
|
"advancedSettings": "Impostazioni avanzate",
|
||||||
"searchModelsPlaceholder": "Cerca modelli..."
|
"searchModelsPlaceholder": "Cerca modelli...",
|
||||||
|
"showThinking": "Mostra il processo di pensiero",
|
||||||
|
"hideThinking": "Nascondi il processo di pensiero"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"settingsTitle": "Impostazioni utente",
|
"settingsTitle": "Impostazioni utente",
|
||||||
|
|
@ -283,5 +288,19 @@
|
||||||
"enabled": "Abilitato",
|
"enabled": "Abilitato",
|
||||||
"disabled": "Disabilitato",
|
"disabled": "Disabilitato",
|
||||||
"startNewChat": "Avvia una nuova chat"
|
"startNewChat": "Avvia una nuova chat"
|
||||||
|
},
|
||||||
|
"RAG": {
|
||||||
|
"prompt": "Prompt",
|
||||||
|
"searchingFiles": "Ricerca dei file in corso...",
|
||||||
|
"usingTool": "Utilizzo di {{tool}}...",
|
||||||
|
"saveAndSend": "Salva e invia",
|
||||||
|
"cancel": "Annulla",
|
||||||
|
"hideFiles": "Nascondi file",
|
||||||
|
"retrievalEnabled": "Il recupero dei file è abilitato sui file selezionati per questo messaggio. Clicca sull'indicatore per disattivare.",
|
||||||
|
"retrievalDisabled": "Clicca sull'indicatore per abilitare il recupero dei file per questo messaggio.",
|
||||||
|
"viewFiles": "Visualizza {{count}} {{count, plural, one {file} other {file}}}",
|
||||||
|
"sourceCount": "Numero di fonti:",
|
||||||
|
"adjustRetrievalSettings": "Regola le impostazioni di recupero.",
|
||||||
|
"saveAndClose": "Salva e Chiudi"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
"back": "戻る",
|
"back": "戻る",
|
||||||
"next": "次へ",
|
"next": "次へ",
|
||||||
"WelcomeToChatDeskUI": "対話工房へようこそ — AIチャット開発のためのフロントエンドプラットフォーム",
|
"WelcomeToChatDeskUI": "対話工房へようこそ — AIチャット開発のためのフロントエンドプラットフォーム",
|
||||||
"ClickNextToStartChatting": "「次へ」をクリックして、対話工房でLLMとのチャットを始めましょう。"
|
"ClickNextToStartChatting": "「次へ」をクリックして、対話工房でLLMとのチャットを始めましょう。",
|
||||||
|
"Default": "デフォルト",
|
||||||
|
"DefaultLLMModelCreatedDuringOnboarding": "初期設定時に作成されたデフォルトのLLMモデル"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"email": "メールアドレス",
|
"email": "メールアドレス",
|
||||||
|
|
@ -61,7 +63,8 @@
|
||||||
"signupNotAllowed": "メールアドレス {{email}} は登録できません。",
|
"signupNotAllowed": "メールアドレス {{email}} は登録できません。",
|
||||||
"unexpectedError": "予期しないエラーが発生しました",
|
"unexpectedError": "予期しないエラーが発生しました",
|
||||||
"invalidCredentials": "メールアドレスまたはパスワードが正しくありません。",
|
"invalidCredentials": "メールアドレスまたはパスワードが正しくありません。",
|
||||||
"clearing": "ログイン状態をクリアしています..."
|
"clearing": "ログイン状態をクリアしています...",
|
||||||
|
"sessionExpired": "セッションの有効期限が切れました。再登録またはログインしてください。"
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"defaultChatTitle": "会話",
|
"defaultChatTitle": "会話",
|
||||||
|
|
@ -85,7 +88,9 @@
|
||||||
"selectModel": "モデルを選択",
|
"selectModel": "モデルを選択",
|
||||||
"hosted": "ホステッド",
|
"hosted": "ホステッド",
|
||||||
"advancedSettings": "高度な設定",
|
"advancedSettings": "高度な設定",
|
||||||
"searchModelsPlaceholder": "モデルを検索..."
|
"searchModelsPlaceholder": "モデルを検索...",
|
||||||
|
"showThinking": "思考過程を表示",
|
||||||
|
"hideThinking": "思考過程を非表示"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"settingsTitle": "ユーザー設定",
|
"settingsTitle": "ユーザー設定",
|
||||||
|
|
@ -283,6 +288,20 @@
|
||||||
"enabled": "有効",
|
"enabled": "有効",
|
||||||
"disabled": "無効",
|
"disabled": "無効",
|
||||||
"startNewChat": "新しいチャットを開始"
|
"startNewChat": "新しいチャットを開始"
|
||||||
|
},
|
||||||
|
"RAG": {
|
||||||
|
"prompt": "プロンプト",
|
||||||
|
"searchingFiles": "ファイルを検索中...",
|
||||||
|
"usingTool": "{{tool}} を使用中...",
|
||||||
|
"saveAndSend": "保存して送信",
|
||||||
|
"cancel": "キャンセル",
|
||||||
|
"hideFiles": "ファイルを非表示",
|
||||||
|
"retrievalEnabled": "このメッセージでは選択されたファイルの取得が有効です。無効にするにはインジケーターをクリックしてください。",
|
||||||
|
"retrievalDisabled": "このメッセージのファイル取得を有効にするには、インジケーターをクリックしてください。",
|
||||||
|
"viewFiles": "{{count}} 件のファイルを表示",
|
||||||
|
"sourceCount": "ソース数:",
|
||||||
|
"adjustRetrievalSettings": "取得設定を調整する。",
|
||||||
|
"saveAndClose": "保存して閉じる"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
"back": "뒤로",
|
"back": "뒤로",
|
||||||
"next": "다음",
|
"next": "다음",
|
||||||
"WelcomeToChatDeskUI": "ChatDesk에 오신 것을 환영합니다. AI 챗 R&D를 위한 프론트엔드 플랫폼입니다.",
|
"WelcomeToChatDeskUI": "ChatDesk에 오신 것을 환영합니다. AI 챗 R&D를 위한 프론트엔드 플랫폼입니다.",
|
||||||
"ClickNextToStartChatting": "'다음'을 클릭하여 ChatDesk를 통해 LLM과의 채팅을 시작하세요."
|
"ClickNextToStartChatting": "'다음'을 클릭하여 ChatDesk를 통해 LLM과의 채팅을 시작하세요.",
|
||||||
|
"Default": "기본값",
|
||||||
|
"DefaultLLMModelCreatedDuringOnboarding": "온보딩 중에 생성된 기본 LLM 모델"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"email": "이메일",
|
"email": "이메일",
|
||||||
|
|
@ -61,7 +63,8 @@
|
||||||
"signupNotAllowed": "이메일 {{email}}은(는) 가입할 수 없습니다.",
|
"signupNotAllowed": "이메일 {{email}}은(는) 가입할 수 없습니다.",
|
||||||
"unexpectedError": "예상치 못한 오류가 발생했습니다.",
|
"unexpectedError": "예상치 못한 오류가 발생했습니다.",
|
||||||
"invalidCredentials": "이메일 또는 비밀번호가 올바르지 않습니다.",
|
"invalidCredentials": "이메일 또는 비밀번호가 올바르지 않습니다.",
|
||||||
"clearing": "로그인 상태를 정리하는 중..."
|
"clearing": "로그인 상태를 정리하는 중...",
|
||||||
|
"sessionExpired": "세션이 만료되었습니다. 다시 가입하거나 로그인해주세요."
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"defaultChatTitle": "채팅",
|
"defaultChatTitle": "채팅",
|
||||||
|
|
@ -85,7 +88,9 @@
|
||||||
"selectModel": "모델 선택",
|
"selectModel": "모델 선택",
|
||||||
"hosted": "호스팅됨",
|
"hosted": "호스팅됨",
|
||||||
"advancedSettings": "고급 설정",
|
"advancedSettings": "고급 설정",
|
||||||
"searchModelsPlaceholder": "모델 검색..."
|
"searchModelsPlaceholder": "모델 검색...",
|
||||||
|
"showThinking": "생각 과정을 표시",
|
||||||
|
"hideThinking": "생각 과정을 숨기기"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"settingsTitle": "사용자 설정",
|
"settingsTitle": "사용자 설정",
|
||||||
|
|
@ -283,5 +288,19 @@
|
||||||
"enabled": "활성화됨",
|
"enabled": "활성화됨",
|
||||||
"disabled": "비활성화됨",
|
"disabled": "비활성화됨",
|
||||||
"startNewChat": "새 채팅 시작"
|
"startNewChat": "새 채팅 시작"
|
||||||
|
},
|
||||||
|
"RAG": {
|
||||||
|
"prompt": "프롬프트",
|
||||||
|
"searchingFiles": "파일 검색 중...",
|
||||||
|
"usingTool": "{{tool}} 사용 중...",
|
||||||
|
"saveAndSend": "저장 및 전송",
|
||||||
|
"cancel": "취소",
|
||||||
|
"hideFiles": "파일 숨기기",
|
||||||
|
"retrievalEnabled": "이 메시지에 대해 선택한 파일에서 파일 검색이 활성화되었습니다. 비활성화하려면 표시기를 클릭하세요.",
|
||||||
|
"retrievalDisabled": "이 메시지에 대해 파일 검색을 활성화하려면 표시기를 클릭하세요.",
|
||||||
|
"viewFiles": "{{count}}개의 파일 보기",
|
||||||
|
"sourceCount": "출처 수:",
|
||||||
|
"adjustRetrievalSettings": "검색 설정 조정.",
|
||||||
|
"saveAndClose": "저장 후 닫기"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
"back": "Voltar",
|
"back": "Voltar",
|
||||||
"next": "Próximo",
|
"next": "Próximo",
|
||||||
"WelcomeToChatDeskUI": "Bem-vindo ao ChatDesk, a plataforma frontend para P&D de chat com IA",
|
"WelcomeToChatDeskUI": "Bem-vindo ao ChatDesk, a plataforma frontend para P&D de chat com IA",
|
||||||
"ClickNextToStartChatting": "Clique em 'Próximo' para começar a conversar com seu LLM usando o ChatDesk."
|
"ClickNextToStartChatting": "Clique em 'Próximo' para começar a conversar com seu LLM usando o ChatDesk.",
|
||||||
|
"Default": "Padrão",
|
||||||
|
"DefaultLLMModelCreatedDuringOnboarding": "Modelo LLM padrão criado durante a integração"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
|
|
@ -61,7 +63,8 @@
|
||||||
"signupNotAllowed": "O email {{email}} não está autorizado a se registrar.",
|
"signupNotAllowed": "O email {{email}} não está autorizado a se registrar.",
|
||||||
"unexpectedError": "Ocorreu um erro inesperado",
|
"unexpectedError": "Ocorreu um erro inesperado",
|
||||||
"invalidCredentials": "Email ou senha inválidos.",
|
"invalidCredentials": "Email ou senha inválidos.",
|
||||||
"clearing": "Limpando o estado de login..."
|
"clearing": "Limpando o estado de login...",
|
||||||
|
"sessionExpired": "Sua sessão expirou. Por favor, cadastre-se ou faça login novamente."
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"defaultChatTitle": "Chat",
|
"defaultChatTitle": "Chat",
|
||||||
|
|
@ -85,7 +88,9 @@
|
||||||
"selectModel": "Selecionar modelo",
|
"selectModel": "Selecionar modelo",
|
||||||
"hosted": "Hospedado",
|
"hosted": "Hospedado",
|
||||||
"advancedSettings": "Configurações Avançadas",
|
"advancedSettings": "Configurações Avançadas",
|
||||||
"searchModelsPlaceholder": "Pesquisar modelos..."
|
"searchModelsPlaceholder": "Pesquisar modelos...",
|
||||||
|
"showThinking": "Mostrar processo de pensamento",
|
||||||
|
"hideThinking": "Ocultar processo de pensamento"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"settingsTitle": "Configurações do Usuário",
|
"settingsTitle": "Configurações do Usuário",
|
||||||
|
|
@ -283,5 +288,19 @@
|
||||||
"enabled": "Ativado",
|
"enabled": "Ativado",
|
||||||
"disabled": "Desativado",
|
"disabled": "Desativado",
|
||||||
"startNewChat": "Iniciar nova conversa"
|
"startNewChat": "Iniciar nova conversa"
|
||||||
|
},
|
||||||
|
"RAG": {
|
||||||
|
"prompt": "Prompt",
|
||||||
|
"searchingFiles": "Procurando arquivos...",
|
||||||
|
"usingTool": "Usando {{tool}}...",
|
||||||
|
"saveAndSend": "Salvar e Enviar",
|
||||||
|
"cancel": "Cancelar",
|
||||||
|
"hideFiles": "Ocultar arquivos",
|
||||||
|
"retrievalEnabled": "A recuperação de arquivos está ativada nos arquivos selecionados para esta mensagem. Clique no indicador para desativar.",
|
||||||
|
"retrievalDisabled": "Clique no indicador para ativar a recuperação de arquivos para esta mensagem.",
|
||||||
|
"viewFiles": "Ver {{count}} {{count, plural, one {arquivo} other {arquivos}}}",
|
||||||
|
"sourceCount": "Quantidade de fontes:",
|
||||||
|
"adjustRetrievalSettings": "Ajustar configurações de recuperação.",
|
||||||
|
"saveAndClose": "Salvar e Fechar"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
"back": "Назад",
|
"back": "Назад",
|
||||||
"next": "Далее",
|
"next": "Далее",
|
||||||
"WelcomeToChatDeskUI": "Добро пожаловать в ChatDesk — фронтенд-платформу для исследований и разработок в области чат-ИИ",
|
"WelcomeToChatDeskUI": "Добро пожаловать в ChatDesk — фронтенд-платформу для исследований и разработок в области чат-ИИ",
|
||||||
"ClickNextToStartChatting": "Нажмите «Далее», чтобы начать общение с вашей LLM через ChatDesk."
|
"ClickNextToStartChatting": "Нажмите «Далее», чтобы начать общение с вашей LLM через ChatDesk.",
|
||||||
|
"Default": "По умолчанию",
|
||||||
|
"DefaultLLMModelCreatedDuringOnboarding": "Модель LLM по умолчанию, созданная во время начальной настройки"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"email": "Эл. почта",
|
"email": "Эл. почта",
|
||||||
|
|
@ -61,7 +63,8 @@
|
||||||
"signupNotAllowed": "Электронная почта {{email}} не разрешена для регистрации.",
|
"signupNotAllowed": "Электронная почта {{email}} не разрешена для регистрации.",
|
||||||
"unexpectedError": "Произошла непредвиденная ошибка",
|
"unexpectedError": "Произошла непредвиденная ошибка",
|
||||||
"invalidCredentials": "Неверный адрес эл. почты или пароль.",
|
"invalidCredentials": "Неверный адрес эл. почты или пароль.",
|
||||||
"clearing": "Очистка состояния входа..."
|
"clearing": "Очистка состояния входа...",
|
||||||
|
"sessionExpired": "Ваша сессия истекла. Пожалуйста, зарегистрируйтесь или войдите снова."
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"defaultChatTitle": "Чат",
|
"defaultChatTitle": "Чат",
|
||||||
|
|
@ -85,7 +88,9 @@
|
||||||
"selectModel": "Выбрать модель",
|
"selectModel": "Выбрать модель",
|
||||||
"hosted": "Хостинг",
|
"hosted": "Хостинг",
|
||||||
"advancedSettings": "Дополнительные настройки",
|
"advancedSettings": "Дополнительные настройки",
|
||||||
"searchModelsPlaceholder": "Поиск моделей..."
|
"searchModelsPlaceholder": "Поиск моделей...",
|
||||||
|
"showThinking": "Показать ход мыслей",
|
||||||
|
"hideThinking": "Скрыть ход мыслей"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"settingsTitle": "Настройки пользователя",
|
"settingsTitle": "Настройки пользователя",
|
||||||
|
|
@ -283,5 +288,19 @@
|
||||||
"enabled": "Включено",
|
"enabled": "Включено",
|
||||||
"disabled": "Отключено",
|
"disabled": "Отключено",
|
||||||
"startNewChat": "Начать новый чат"
|
"startNewChat": "Начать новый чат"
|
||||||
|
},
|
||||||
|
"RAG": {
|
||||||
|
"prompt": "Подсказка",
|
||||||
|
"searchingFiles": "Поиск файлов...",
|
||||||
|
"usingTool": "Используется {{tool}}...",
|
||||||
|
"saveAndSend": "Сохранить и отправить",
|
||||||
|
"cancel": "Отмена",
|
||||||
|
"hideFiles": "Скрыть файлы",
|
||||||
|
"retrievalEnabled": "Извлечение файлов включено для выбранных файлов этого сообщения. Нажмите на индикатор, чтобы отключить.",
|
||||||
|
"retrievalDisabled": "Нажмите на индикатор, чтобы включить извлечение файлов для этого сообщения.",
|
||||||
|
"viewFiles": "Просмотреть {{count}} {{count, plural, one {файл} few {файла} many {файлов} other {файла}}}",
|
||||||
|
"sourceCount": "Количество источников:",
|
||||||
|
"adjustRetrievalSettings": "Настроить параметры извлечения.",
|
||||||
|
"saveAndClose": "Сохранить и закрыть"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
"back": "ආපසු",
|
"back": "ආපසු",
|
||||||
"next": "ඊළඟ",
|
"next": "ඊළඟ",
|
||||||
"WelcomeToChatDeskUI": "ChatDesk වෙත සාදරයෙන් පිළිගනිමු — AI කථා බස සොයාගැනීම් සහ සංවර්ධනය සඳහා ඉදිරිපස වේදිකාව",
|
"WelcomeToChatDeskUI": "ChatDesk වෙත සාදරයෙන් පිළිගනිමු — AI කථා බස සොයාගැනීම් සහ සංවර්ධනය සඳහා ඉදිරිපස වේදිකාව",
|
||||||
"ClickNextToStartChatting": "ඔබගේ LLM සමඟ ChatDesk භාවිතයෙන් කතා කිරීම ආරම්භ කිරීමට 'ඊළඟ' ක්ලික් කරන්න."
|
"ClickNextToStartChatting": "ඔබගේ LLM සමඟ ChatDesk භාවිතයෙන් කතා කිරීම ආරම්භ කිරීමට 'ඊළඟ' ක්ලික් කරන්න.",
|
||||||
|
"Default": "පෙරනිමි",
|
||||||
|
"DefaultLLMModelCreatedDuringOnboarding": "ඇරඹුම් අවස්ථාවේදී නිර්මාණය කරන ලද පෙරනිමි LLM ආදර්ශය"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"email": "ඊමේල්",
|
"email": "ඊමේල්",
|
||||||
|
|
@ -61,7 +63,8 @@
|
||||||
"signupNotAllowed": "{{email}} යන්න ලියාපදිංචි වීමට අවසර නැත.",
|
"signupNotAllowed": "{{email}} යන්න ලියාපදිංචි වීමට අවසර නැත.",
|
||||||
"unexpectedError": "予期 නොකළ දෝෂයක් ඇතිවිය",
|
"unexpectedError": "予期 නොකළ දෝෂයක් ඇතිවිය",
|
||||||
"invalidCredentials": "අවලංගු ඊමේල් හෝ මුරපදයක්.",
|
"invalidCredentials": "අවලංගු ඊමේල් හෝ මුරපදයක්.",
|
||||||
"clearing": "ඇතුල් වීමේ තත්වය මකාදමමින්..."
|
"clearing": "ඇතුල් වීමේ තත්වය මකාදමමින්...",
|
||||||
|
"sessionExpired": "ඔබගේ සැසිය අවසන් වී ඇත. කරුණාකර නැවත ලියාපදිංචි වන්න හෝ පිවිසෙන්න."
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"defaultChatTitle": "කතාබස්",
|
"defaultChatTitle": "කතාබස්",
|
||||||
|
|
@ -85,7 +88,9 @@
|
||||||
"selectModel": "මාදිලිය තෝරන්න",
|
"selectModel": "මාදිලිය තෝරන්න",
|
||||||
"hosted": "සත්කාරකකළ",
|
"hosted": "සත්කාරකකළ",
|
||||||
"advancedSettings": "උසස් සැකසුම්",
|
"advancedSettings": "උසස් සැකසුම්",
|
||||||
"searchModelsPlaceholder": "මාදිලි සෙවීම..."
|
"searchModelsPlaceholder": "මාදිලි සෙවීම...",
|
||||||
|
"showThinking": "සිතීමේ ක්රියාවලිය පෙන්වන්න",
|
||||||
|
"hideThinking": "සිතීමේ ක්රියාවලිය සඟවන්න"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"settingsTitle": "පරිශීලක සැකසුම්",
|
"settingsTitle": "පරිශීලක සැකසුම්",
|
||||||
|
|
@ -283,5 +288,19 @@
|
||||||
"enabled": "සක්රිය",
|
"enabled": "සක්රිය",
|
||||||
"disabled": "අක්රිය",
|
"disabled": "අක්රිය",
|
||||||
"startNewChat": "නව කතාබස් ආරම්භ කරන්න"
|
"startNewChat": "නව කතාබස් ආරම්භ කරන්න"
|
||||||
|
},
|
||||||
|
"RAG": {
|
||||||
|
"prompt": "ප්රෝම්ප්ට්",
|
||||||
|
"searchingFiles": "ගොනු සෙවීම...",
|
||||||
|
"usingTool": "{{tool}} භාවිතා කරමින්...",
|
||||||
|
"saveAndSend": "සුරකින්න සහ යවන්න",
|
||||||
|
"cancel": "අවලංගු කරන්න",
|
||||||
|
"hideFiles": "ගොනු සඟවන්න",
|
||||||
|
"retrievalEnabled": "මෙම පණිවිඩය සඳහා තෝරාගත් ගොනු සඳහා ගොනු සෙවීම සක්රිය කර ඇත. අක්රිය කිරීමට දර්ශකය ක්ලික් කරන්න.",
|
||||||
|
"retrievalDisabled": "මෙම පණිවිඩය සඳහා ගොනු සෙවීම සක්රිය කිරීමට දර්ශකය ක්ලික් කරන්න.",
|
||||||
|
"viewFiles": "{{count}} {{count, plural, one {ගොනුවක්} other {ගොනු}}} බලන්න",
|
||||||
|
"sourceCount": "මූලාශ්ර ගණන:",
|
||||||
|
"adjustRetrievalSettings": "ප්රතිසාධන සැකසුම් සෙවීම සකසන්න.",
|
||||||
|
"saveAndClose": "සුරකින්න සහ වසන්න"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
"back": "Tillbaka",
|
"back": "Tillbaka",
|
||||||
"next": "Nästa",
|
"next": "Nästa",
|
||||||
"WelcomeToChatDeskUI": "Välkommen till ChatDesk – frontendlösningen för AI-chattforskning och utveckling",
|
"WelcomeToChatDeskUI": "Välkommen till ChatDesk – frontendlösningen för AI-chattforskning och utveckling",
|
||||||
"ClickNextToStartChatting": "Klicka på 'Nästa' för att börja chatta med din LLM via ChatDesk."
|
"ClickNextToStartChatting": "Klicka på 'Nästa' för att börja chatta med din LLM via ChatDesk.",
|
||||||
|
"Default": "Standard",
|
||||||
|
"DefaultLLMModelCreatedDuringOnboarding": "Standard LLM-modell skapad under introduktionen"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"email": "E-post",
|
"email": "E-post",
|
||||||
|
|
@ -61,7 +63,8 @@
|
||||||
"signupNotAllowed": "E-postadressen {{email}} är inte tillåten för registrering.",
|
"signupNotAllowed": "E-postadressen {{email}} är inte tillåten för registrering.",
|
||||||
"unexpectedError": "Ett oväntat fel inträffade",
|
"unexpectedError": "Ett oväntat fel inträffade",
|
||||||
"invalidCredentials": "Ogiltig e-postadress eller lösenord.",
|
"invalidCredentials": "Ogiltig e-postadress eller lösenord.",
|
||||||
"clearing": "Rensar inloggningsstatus..."
|
"clearing": "Rensar inloggningsstatus...",
|
||||||
|
"sessionExpired": "Din session har gått ut. Registrera dig eller logga in igen."
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"defaultChatTitle": "Chatt",
|
"defaultChatTitle": "Chatt",
|
||||||
|
|
@ -85,7 +88,9 @@
|
||||||
"selectModel": "Välj en modell",
|
"selectModel": "Välj en modell",
|
||||||
"hosted": "Hostad",
|
"hosted": "Hostad",
|
||||||
"advancedSettings": "Avancerade inställningar",
|
"advancedSettings": "Avancerade inställningar",
|
||||||
"searchModelsPlaceholder": "Sök modeller..."
|
"searchModelsPlaceholder": "Sök modeller...",
|
||||||
|
"showThinking": "Visa tankegång",
|
||||||
|
"hideThinking": "Dölj tankegång"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"settingsTitle": "Användarinställningar",
|
"settingsTitle": "Användarinställningar",
|
||||||
|
|
@ -283,5 +288,19 @@
|
||||||
"enabled": "Aktiverad",
|
"enabled": "Aktiverad",
|
||||||
"disabled": "Avaktiverad",
|
"disabled": "Avaktiverad",
|
||||||
"startNewChat": "Starta ny chatt"
|
"startNewChat": "Starta ny chatt"
|
||||||
|
},
|
||||||
|
"RAG": {
|
||||||
|
"prompt": "Prompt",
|
||||||
|
"searchingFiles": "Söker efter filer...",
|
||||||
|
"usingTool": "Använder {{tool}}...",
|
||||||
|
"saveAndSend": "Spara och skicka",
|
||||||
|
"cancel": "Avbryt",
|
||||||
|
"hideFiles": "Dölj filer",
|
||||||
|
"retrievalEnabled": "Filhämtning är aktiverat för de valda filerna för detta meddelande. Klicka på indikatorn för att inaktivera.",
|
||||||
|
"retrievalDisabled": "Klicka på indikatorn för att aktivera filhämtning för detta meddelande.",
|
||||||
|
"viewFiles": "Visa {{count}} {{count, plural, one {fil} other {filer}}}",
|
||||||
|
"sourceCount": "Antal källor:",
|
||||||
|
"adjustRetrievalSettings": "Justera hämtningsinställningar.",
|
||||||
|
"saveAndClose": "Spara och stäng"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
"back": "వెనక్కి",
|
"back": "వెనక్కి",
|
||||||
"next": "తరువాత",
|
"next": "తరువాత",
|
||||||
"WelcomeToChatDeskUI": "ChatDesk కు స్వాగతం — ఇది AI చాట్ R&D కోసం ముందు భాగం ప్లాట్ఫారమ్",
|
"WelcomeToChatDeskUI": "ChatDesk కు స్వాగతం — ఇది AI చాట్ R&D కోసం ముందు భాగం ప్లాట్ఫారమ్",
|
||||||
"ClickNextToStartChatting": "'తరువాత' క్లిక్ చేసి ChatDesk ద్వారా మీ LLMతో చాట్ చేయడం ప్రారంభించండి."
|
"ClickNextToStartChatting": "'తరువాత' క్లిక్ చేసి ChatDesk ద్వారా మీ LLMతో చాట్ చేయడం ప్రారంభించండి.",
|
||||||
|
"Default": "డీఫాల్ట్",
|
||||||
|
"DefaultLLMModelCreatedDuringOnboarding": "ఆన్బోర్డింగ్ సమయంలో సృష్టించబడిన డీఫాల్ట్ LLM మోడల్"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"email": "ఈమెయిల్",
|
"email": "ఈమెయిల్",
|
||||||
|
|
@ -61,7 +63,8 @@
|
||||||
"signupNotAllowed": "{{email}} చిరునామా నమోదు చేసుకోడానికి అనుమతించబడలేదు.",
|
"signupNotAllowed": "{{email}} చిరునామా నమోదు చేసుకోడానికి అనుమతించబడలేదు.",
|
||||||
"unexpectedError": "అనుకోని లోపం సంభవించింది",
|
"unexpectedError": "అనుకోని లోపం సంభవించింది",
|
||||||
"invalidCredentials": "చెల్లని ఈమెయిల్ లేదా పాస్వర్డ్.",
|
"invalidCredentials": "చెల్లని ఈమెయిల్ లేదా పాస్వర్డ్.",
|
||||||
"clearing": "లాగిన్ స్థితిని క్లియర్ చేస్తోంది..."
|
"clearing": "లాగిన్ స్థితిని క్లియర్ చేస్తోంది...",
|
||||||
|
"sessionExpired": "మీ సెషన్ గడువు ముగిసింది. దయచేసి మళ్లీ నమోదు చేసుకోండి లేదా లాగిన్ అవ్వండి."
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"defaultChatTitle": "చాట్",
|
"defaultChatTitle": "చాట్",
|
||||||
|
|
@ -85,7 +88,9 @@
|
||||||
"selectModel": "ఒక మోడల్ను ఎంచుకోండి",
|
"selectModel": "ఒక మోడల్ను ఎంచుకోండి",
|
||||||
"hosted": "హోస్ట్ చేయబడింది",
|
"hosted": "హోస్ట్ చేయబడింది",
|
||||||
"advancedSettings": "అధునాతన సెట్టింగులు",
|
"advancedSettings": "అధునాతన సెట్టింగులు",
|
||||||
"searchModelsPlaceholder": "మోడల్స్ శోధించండి..."
|
"searchModelsPlaceholder": "మోడల్స్ శోధించండి...",
|
||||||
|
"showThinking": "ఆలోచనా ప్రక్రియ చూపించు",
|
||||||
|
"hideThinking": "ఆలోచనా ప్రక్రియ దాచు"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"settingsTitle": "వినియోగదారు సెట్టింగులు",
|
"settingsTitle": "వినియోగదారు సెట్టింగులు",
|
||||||
|
|
@ -283,5 +288,19 @@
|
||||||
"enabled": "ప్రదర్శించబడింది",
|
"enabled": "ప్రదర్శించబడింది",
|
||||||
"disabled": "అచేతనం",
|
"disabled": "అచేతనం",
|
||||||
"startNewChat": "కొత్త చాట్ ప్రారంభించు"
|
"startNewChat": "కొత్త చాట్ ప్రారంభించు"
|
||||||
|
},
|
||||||
|
"RAG": {
|
||||||
|
"prompt": "ప్రాంప్ట్",
|
||||||
|
"searchingFiles": "ఫైళ్లను శోధిస్తోంది...",
|
||||||
|
"usingTool": "{{tool}} ఉపయోగిస్తోంది...",
|
||||||
|
"saveAndSend": "సేవ్ చేసి పంపు",
|
||||||
|
"cancel": "రద్దు చేయి",
|
||||||
|
"hideFiles": "ఫైళ్లను దాచు",
|
||||||
|
"retrievalEnabled": "ఈ సందేశానికి ఎంపిక చేసిన ఫైళ్లపై ఫైల్ రిట్రీవల్ ప్రారంభించబడింది. నిలిపివేయడానికి సూచికపై క్లిక్ చేయండి.",
|
||||||
|
"retrievalDisabled": "ఈ సందేశానికి ఫైల్ రిట్రీవల్ ప్రారంభించడానికి సూచికపై క్లిక్ చేయండి.",
|
||||||
|
"viewFiles": "{{count}} {{count, plural, one {ఫైల్} other {ఫైల్స్}}} చూపించు",
|
||||||
|
"sourceCount": "మూలాల సంఖ్య:",
|
||||||
|
"adjustRetrievalSettings": "రిట్రీవల్ సెట్టింగులను సర్దుబాటు చేయండి.",
|
||||||
|
"saveAndClose": "సేవ్ చేసి మూసివేయండి"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
"back": "Quay lại",
|
"back": "Quay lại",
|
||||||
"next": "Tiếp theo",
|
"next": "Tiếp theo",
|
||||||
"WelcomeToChatDeskUI": "Chào mừng đến với ChatDesk – nền tảng giao diện dành cho nghiên cứu và phát triển trò chuyện AI",
|
"WelcomeToChatDeskUI": "Chào mừng đến với ChatDesk – nền tảng giao diện dành cho nghiên cứu và phát triển trò chuyện AI",
|
||||||
"ClickNextToStartChatting": "Nhấn 'Tiếp theo' để bắt đầu trò chuyện với LLM của bạn qua ChatDesk."
|
"ClickNextToStartChatting": "Nhấn 'Tiếp theo' để bắt đầu trò chuyện với LLM của bạn qua ChatDesk.",
|
||||||
|
"Default": "Mặc định",
|
||||||
|
"DefaultLLMModelCreatedDuringOnboarding": "Mô hình LLM mặc định được tạo trong quá trình thiết lập"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
|
|
@ -61,7 +63,8 @@
|
||||||
"signupNotAllowed": "Email {{email}} không được phép đăng ký.",
|
"signupNotAllowed": "Email {{email}} không được phép đăng ký.",
|
||||||
"unexpectedError": "Đã xảy ra lỗi không mong muốn",
|
"unexpectedError": "Đã xảy ra lỗi không mong muốn",
|
||||||
"invalidCredentials": "Email hoặc mật khẩu không hợp lệ.",
|
"invalidCredentials": "Email hoặc mật khẩu không hợp lệ.",
|
||||||
"clearing": "Đang xóa trạng thái đăng nhập..."
|
"clearing": "Đang xóa trạng thái đăng nhập...",
|
||||||
|
"sessionExpired": "Phiên của bạn đã hết hạn. Vui lòng đăng ký hoặc đăng nhập lại."
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"defaultChatTitle": "Trò chuyện",
|
"defaultChatTitle": "Trò chuyện",
|
||||||
|
|
@ -85,7 +88,9 @@
|
||||||
"selectModel": "Chọn một mô hình",
|
"selectModel": "Chọn một mô hình",
|
||||||
"hosted": "Được lưu trữ",
|
"hosted": "Được lưu trữ",
|
||||||
"advancedSettings": "Cài đặt nâng cao",
|
"advancedSettings": "Cài đặt nâng cao",
|
||||||
"searchModelsPlaceholder": "Tìm kiếm mô hình..."
|
"searchModelsPlaceholder": "Tìm kiếm mô hình...",
|
||||||
|
"showThinking": "Hiển thị quá trình suy nghĩ",
|
||||||
|
"hideThinking": "Ẩn quá trình suy nghĩ"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"settingsTitle": "Cài đặt người dùng",
|
"settingsTitle": "Cài đặt người dùng",
|
||||||
|
|
@ -283,5 +288,19 @@
|
||||||
"enabled": "Đã bật",
|
"enabled": "Đã bật",
|
||||||
"disabled": "Đã tắt",
|
"disabled": "Đã tắt",
|
||||||
"startNewChat": "Bắt đầu cuộc trò chuyện mới"
|
"startNewChat": "Bắt đầu cuộc trò chuyện mới"
|
||||||
|
},
|
||||||
|
"RAG": {
|
||||||
|
"prompt": "Gợi ý",
|
||||||
|
"searchingFiles": "Đang tìm kiếm tệp...",
|
||||||
|
"usingTool": "Đang sử dụng {{tool}}...",
|
||||||
|
"saveAndSend": "Lưu và gửi",
|
||||||
|
"cancel": "Hủy",
|
||||||
|
"hideFiles": "Ẩn tệp",
|
||||||
|
"retrievalEnabled": "Truy xuất tệp đã được bật cho các tệp đã chọn trong tin nhắn này. Nhấn vào biểu tượng để tắt.",
|
||||||
|
"retrievalDisabled": "Nhấn vào biểu tượng để bật truy xuất tệp cho tin nhắn này.",
|
||||||
|
"viewFiles": "Xem {{count}} tệp",
|
||||||
|
"sourceCount": "Số lượng nguồn:",
|
||||||
|
"adjustRetrievalSettings": "Điều chỉnh cài đặt truy xuất.",
|
||||||
|
"saveAndClose": "Lưu và Đóng"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
"back": "返回",
|
"back": "返回",
|
||||||
"next": "下一步",
|
"next": "下一步",
|
||||||
"WelcomeToChatDeskUI": "欢迎使用 对话工坊 —— 面向 AI 对话研发的前端平台",
|
"WelcomeToChatDeskUI": "欢迎使用 对话工坊 —— 面向 AI 对话研发的前端平台",
|
||||||
"ClickNextToStartChatting": "点击“下一步”,开始使用对话工坊与您的 LLM 进行对话。"
|
"ClickNextToStartChatting": "点击“下一步”,开始使用对话工坊与您的 LLM 进行对话。",
|
||||||
|
"Default": "默认",
|
||||||
|
"DefaultLLMModelCreatedDuringOnboarding": "在初始化过程中创建的默认LLM模型"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"email": "电子邮件",
|
"email": "电子邮件",
|
||||||
|
|
@ -61,7 +63,8 @@
|
||||||
"signupNotAllowed": "邮箱 {{email}} 不允许注册。",
|
"signupNotAllowed": "邮箱 {{email}} 不允许注册。",
|
||||||
"unexpectedError": "发生了未知错误",
|
"unexpectedError": "发生了未知错误",
|
||||||
"invalidCredentials": "邮箱或密码错误。",
|
"invalidCredentials": "邮箱或密码错误。",
|
||||||
"clearing": "正在清除登录状态..."
|
"clearing": "正在清除登录状态...",
|
||||||
|
"sessionExpired": "您的会话已过期,请重新注册或登录。"
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"defaultChatTitle": "对话",
|
"defaultChatTitle": "对话",
|
||||||
|
|
@ -85,7 +88,9 @@
|
||||||
"selectModel": "选择一个模型",
|
"selectModel": "选择一个模型",
|
||||||
"hosted": "托管的",
|
"hosted": "托管的",
|
||||||
"advancedSettings": "高级设置",
|
"advancedSettings": "高级设置",
|
||||||
"searchModelsPlaceholder": "搜索模型..."
|
"searchModelsPlaceholder": "搜索模型...",
|
||||||
|
"showThinking": "显示思考过程",
|
||||||
|
"hideThinking": "隐藏思考过程"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"settingsTitle": "用户设置",
|
"settingsTitle": "用户设置",
|
||||||
|
|
@ -283,5 +288,19 @@
|
||||||
"enabled": "启用",
|
"enabled": "启用",
|
||||||
"disabled": "禁用",
|
"disabled": "禁用",
|
||||||
"startNewChat": "开始新的对话"
|
"startNewChat": "开始新的对话"
|
||||||
|
},
|
||||||
|
"RAG": {
|
||||||
|
"prompt": "提示词",
|
||||||
|
"searchingFiles": "正在搜索文件...",
|
||||||
|
"usingTool": "正在使用 {{tool}}...",
|
||||||
|
"saveAndSend": "保存并发送",
|
||||||
|
"cancel": "取消",
|
||||||
|
"hideFiles": "隐藏文件",
|
||||||
|
"retrievalEnabled": "当前消息启用了所选文件的检索。点击图标以禁用。",
|
||||||
|
"retrievalDisabled": "点击图标以启用此消息的文件检索。",
|
||||||
|
"viewFiles": "查看 {{count}} 个文件",
|
||||||
|
"sourceCount": "来源数量:",
|
||||||
|
"adjustRetrievalSettings": "调整检索设置。",
|
||||||
|
"saveAndClose": "保存并关闭"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,8 @@ LANGUAGE 'plpgsql'
|
||||||
SECURITY DEFINER
|
SECURITY DEFINER
|
||||||
AS $$
|
AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
project_url TEXT := 'http://supabase_kong_chatbotui:8000';
|
project_url TEXT := 'http://localhost:8000';
|
||||||
service_role_key TEXT := 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU'; -- full access needed for http request to storage
|
service_role_key TEXT := 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic2VydmljZV9yb2xlIiwiaXNzIjoic3VwYWJhc2UtZGVtbyIsImV4cCI6MjA2MjkyNTU2NSwiaWF0IjoxNzQ3NTY1NTY1fQ.18Lxnd9JrkNyV9q38l_8oQB8pwtZK8JwpLwpH2b4JaA'; -- full access needed for http request to storage
|
||||||
url TEXT := project_url || '/storage/v1/object/' || bucket || '/' || object;
|
url TEXT := project_url || '/storage/v1/object/' || bucket || '/' || object;
|
||||||
BEGIN
|
BEGIN
|
||||||
SELECT
|
SELECT
|
||||||
|
|
|
||||||
|
|
@ -126,11 +126,11 @@ BEGIN
|
||||||
TRUE,
|
TRUE,
|
||||||
'Home',
|
'Home',
|
||||||
4096,
|
4096,
|
||||||
'gpt-4-1106-preview',
|
'GPT',
|
||||||
'You are a friendly, helpful AI assistant.',
|
'You are a friendly, helpful AI assistant.',
|
||||||
0.5,
|
0.5,
|
||||||
'My home workspace.',
|
'My home workspace.',
|
||||||
'openai',
|
'bge-m3',
|
||||||
TRUE,
|
TRUE,
|
||||||
TRUE,
|
TRUE,
|
||||||
''
|
''
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ create table file_items (
|
||||||
content TEXT NOT NULL,
|
content TEXT NOT NULL,
|
||||||
local_embedding vector(384), -- 384 works for local w/ Xenova/all-MiniLM-L6-v2
|
local_embedding vector(384), -- 384 works for local w/ Xenova/all-MiniLM-L6-v2
|
||||||
openai_embedding vector(1536), -- 1536 for OpenAI
|
openai_embedding vector(1536), -- 1536 for OpenAI
|
||||||
|
bge_m3_embedding vector(1024), -- 1024 for BGE-M3
|
||||||
tokens INT NOT NULL
|
tokens INT NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -32,6 +33,9 @@ CREATE INDEX file_items_embedding_idx ON file_items
|
||||||
CREATE INDEX file_items_local_embedding_idx ON file_items
|
CREATE INDEX file_items_local_embedding_idx ON file_items
|
||||||
USING hnsw (local_embedding vector_cosine_ops);
|
USING hnsw (local_embedding vector_cosine_ops);
|
||||||
|
|
||||||
|
CREATE INDEX file_items_bge_m3_embedding_idx ON file_items
|
||||||
|
USING hnsw (bge_m3_embedding vector_cosine_ops);
|
||||||
|
|
||||||
-- RLS
|
-- RLS
|
||||||
|
|
||||||
ALTER TABLE file_items ENABLE ROW LEVEL SECURITY;
|
ALTER TABLE file_items ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
@ -114,3 +118,64 @@ begin
|
||||||
limit match_count;
|
limit match_count;
|
||||||
end;
|
end;
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
|
-- create function match_file_items_bge_m3 (
|
||||||
|
-- query_embedding vector(1024),
|
||||||
|
-- match_count int DEFAULT null,
|
||||||
|
-- file_ids UUID[] DEFAULT null
|
||||||
|
-- ) returns table (
|
||||||
|
-- id UUID,
|
||||||
|
-- file_id UUID,
|
||||||
|
-- content TEXT,
|
||||||
|
-- tokens INT,
|
||||||
|
-- similarity float
|
||||||
|
-- )
|
||||||
|
-- language plpgsql
|
||||||
|
-- as $$
|
||||||
|
-- #variable_conflict use_column
|
||||||
|
-- begin
|
||||||
|
-- return query
|
||||||
|
-- select
|
||||||
|
-- id,
|
||||||
|
-- file_id,
|
||||||
|
-- content,
|
||||||
|
-- tokens,
|
||||||
|
-- 1 - (file_items.bge_m3_embedding <=> query_embedding) as similarity
|
||||||
|
-- from file_items
|
||||||
|
-- where (file_id = ANY(file_ids))
|
||||||
|
-- order by file_items.bge_m3_embedding <=> query_embedding
|
||||||
|
-- limit match_count;
|
||||||
|
-- end;
|
||||||
|
-- $$;
|
||||||
|
|
||||||
|
create function match_file_items_bge_m3 (
|
||||||
|
query_embedding vector(1024),
|
||||||
|
match_count int DEFAULT null,
|
||||||
|
file_ids UUID[] DEFAULT null
|
||||||
|
) returns table (
|
||||||
|
id UUID,
|
||||||
|
file_id UUID,
|
||||||
|
content TEXT,
|
||||||
|
tokens INT,
|
||||||
|
similarity float
|
||||||
|
)
|
||||||
|
language plpgsql
|
||||||
|
as $$
|
||||||
|
#variable_conflict use_column
|
||||||
|
begin
|
||||||
|
-- 显式设置 schema 搜索路径
|
||||||
|
SET LOCAL search_path = extensions, public;
|
||||||
|
|
||||||
|
return query
|
||||||
|
select
|
||||||
|
id,
|
||||||
|
file_id,
|
||||||
|
content,
|
||||||
|
tokens,
|
||||||
|
1 - (file_items.bge_m3_embedding <=> query_embedding) as similarity
|
||||||
|
from file_items
|
||||||
|
where file_id = ANY(file_ids)
|
||||||
|
order by file_items.bge_m3_embedding <=> query_embedding
|
||||||
|
limit match_count;
|
||||||
|
end;
|
||||||
|
$$;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
-- WORKSPACES
|
-- WORKSPACES
|
||||||
|
|
||||||
UPDATE workspaces
|
UPDATE workspaces
|
||||||
SET default_model = 'gpt-4-turbo-preview'
|
SET default_model = 'GPT'
|
||||||
WHERE default_model = 'gpt-4-1106-preview';
|
WHERE default_model = 'gpt-4-1106-preview';
|
||||||
|
|
||||||
UPDATE workspaces
|
UPDATE workspaces
|
||||||
|
|
@ -11,7 +11,7 @@ WHERE default_model = 'gpt-3.5-turbo-1106';
|
||||||
-- PRESETS
|
-- PRESETS
|
||||||
|
|
||||||
UPDATE presets
|
UPDATE presets
|
||||||
SET model = 'gpt-4-turbo-preview'
|
SET model = 'GPT'
|
||||||
WHERE model = 'gpt-4-1106-preview';
|
WHERE model = 'gpt-4-1106-preview';
|
||||||
|
|
||||||
UPDATE presets
|
UPDATE presets
|
||||||
|
|
@ -21,7 +21,7 @@ WHERE model = 'gpt-3.5-turbo-1106';
|
||||||
-- ASSISTANTS
|
-- ASSISTANTS
|
||||||
|
|
||||||
UPDATE assistants
|
UPDATE assistants
|
||||||
SET model = 'gpt-4-turbo-preview'
|
SET model = 'GPT'
|
||||||
WHERE model = 'gpt-4-1106-preview';
|
WHERE model = 'gpt-4-1106-preview';
|
||||||
|
|
||||||
UPDATE assistants
|
UPDATE assistants
|
||||||
|
|
@ -31,7 +31,7 @@ WHERE model = 'gpt-3.5-turbo-1106';
|
||||||
-- CHATS
|
-- CHATS
|
||||||
|
|
||||||
UPDATE chats
|
UPDATE chats
|
||||||
SET model = 'gpt-4-turbo-preview'
|
SET model = 'GPT'
|
||||||
WHERE model = 'gpt-4-1106-preview';
|
WHERE model = 'gpt-4-1106-preview';
|
||||||
|
|
||||||
UPDATE chats
|
UPDATE chats
|
||||||
|
|
@ -41,7 +41,7 @@ WHERE model = 'gpt-3.5-turbo-1106';
|
||||||
-- MESSAGES
|
-- MESSAGES
|
||||||
|
|
||||||
UPDATE messages
|
UPDATE messages
|
||||||
SET model = 'gpt-4-turbo-preview'
|
SET model = 'GPT'
|
||||||
WHERE model = 'gpt-4-1106-preview';
|
WHERE model = 'gpt-4-1106-preview';
|
||||||
|
|
||||||
UPDATE messages
|
UPDATE messages
|
||||||
|
|
@ -93,11 +93,11 @@ BEGIN
|
||||||
TRUE,
|
TRUE,
|
||||||
'Home',
|
'Home',
|
||||||
4096,
|
4096,
|
||||||
'gpt-4-turbo-preview', -- Updated default model
|
'GPT', -- Updated default model
|
||||||
'You are a friendly, helpful AI assistant.',
|
'You are a friendly, helpful AI assistant.',
|
||||||
0.5,
|
0.5,
|
||||||
'My home workspace.',
|
'My home workspace.',
|
||||||
'openai',
|
'bge-m3',
|
||||||
TRUE,
|
TRUE,
|
||||||
TRUE,
|
TRUE,
|
||||||
''
|
''
|
||||||
|
|
|
||||||
|
|
@ -1348,6 +1348,20 @@ export type Database = {
|
||||||
similarity: number
|
similarity: number
|
||||||
}[]
|
}[]
|
||||||
}
|
}
|
||||||
|
match_file_items_bge_m3: {
|
||||||
|
Args: {
|
||||||
|
query_embedding: string
|
||||||
|
match_count?: number
|
||||||
|
file_ids?: string[]
|
||||||
|
}
|
||||||
|
Returns: {
|
||||||
|
id: string
|
||||||
|
file_id: string
|
||||||
|
content: string
|
||||||
|
tokens: number
|
||||||
|
similarity: number
|
||||||
|
}[]
|
||||||
|
}
|
||||||
non_private_assistant_exists: {
|
non_private_assistant_exists: {
|
||||||
Args: {
|
Args: {
|
||||||
p_name: string
|
p_name: string
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ export interface ChatSettings {
|
||||||
contextLength: number
|
contextLength: number
|
||||||
includeProfileContext: boolean
|
includeProfileContext: boolean
|
||||||
includeWorkspaceInstructions: boolean
|
includeWorkspaceInstructions: boolean
|
||||||
embeddingsProvider: "openai" | "local"
|
embeddingsProvider: "openai" | "local" | "bge-m3"
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChatPayload {
|
export interface ChatPayload {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ export type LLMID =
|
||||||
|
|
||||||
// OpenAI Models (UPDATED 5/13/24)
|
// OpenAI Models (UPDATED 5/13/24)
|
||||||
export type OpenAILLMID =
|
export type OpenAILLMID =
|
||||||
|
| "GPT" // API compatiable
|
||||||
| "gpt-4o" // GPT-4o
|
| "gpt-4o" // GPT-4o
|
||||||
| "gpt-4-turbo-preview" // GPT-4 Turbo
|
| "gpt-4-turbo-preview" // GPT-4 Turbo
|
||||||
| "gpt-4-vision-preview" // GPT-4 Vision
|
| "gpt-4-vision-preview" // GPT-4 Vision
|
||||||
|
|
|
||||||
|
|
@ -1,175 +1,248 @@
|
||||||
|
-- ✅ 创建 schema (幂等)
|
||||||
DO $$
|
DO $$
|
||||||
BEGIN
|
BEGIN
|
||||||
IF NOT EXISTS(SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'storage') THEN
|
IF NOT EXISTS (
|
||||||
|
SELECT schema_name FROM information_schema.schemata
|
||||||
|
WHERE schema_name = 'storage'
|
||||||
|
) THEN
|
||||||
CREATE SCHEMA storage;
|
CREATE SCHEMA storage;
|
||||||
END IF;
|
END IF;
|
||||||
END$$;
|
END$$;
|
||||||
|
|
||||||
|
-- ✅ 创建角色和默认权限(幂等)
|
||||||
DO $$
|
DO $$
|
||||||
DECLARE
|
DECLARE
|
||||||
install_roles text = COALESCE(current_setting('storage.install_roles', true), 'true');
|
install_roles text := COALESCE(current_setting('storage.install_roles', true), 'true');
|
||||||
anon_role text = COALESCE(current_setting('storage.anon_role', true), 'anon');
|
anon_role text := COALESCE(current_setting('storage.anon_role', true), 'anon');
|
||||||
authenticated_role text = COALESCE(current_setting('storage.authenticated_role', true), 'authenticated');
|
authenticated_role text := COALESCE(current_setting('storage.authenticated_role', true), 'authenticated');
|
||||||
service_role text = COALESCE(current_setting('storage.service_role', true), 'service_role');
|
service_role text := COALESCE(current_setting('storage.service_role', true), 'service_role');
|
||||||
BEGIN
|
BEGIN
|
||||||
IF install_roles != 'true' THEN
|
IF install_roles != 'true' THEN RETURN; END IF;
|
||||||
RETURN;
|
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = anon_role) THEN
|
||||||
|
EXECUTE 'CREATE ROLE ' || quote_ident(anon_role) || ' NOLOGIN NOINHERIT';
|
||||||
|
END IF;
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = authenticated_role) THEN
|
||||||
|
EXECUTE 'CREATE ROLE ' || quote_ident(authenticated_role) || ' NOLOGIN NOINHERIT';
|
||||||
|
END IF;
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = service_role) THEN
|
||||||
|
EXECUTE 'CREATE ROLE ' || quote_ident(service_role) || ' NOLOGIN NOINHERIT BYPASSRLS';
|
||||||
|
END IF;
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'authenticator') THEN
|
||||||
|
CREATE USER authenticator NOINHERIT;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- Install ROLES
|
EXECUTE 'GRANT ' || quote_ident(anon_role) || ',' ||
|
||||||
EXECUTE 'CREATE ROLE ' || anon_role || ' NOLOGIN NOINHERIT';
|
quote_ident(authenticated_role) || ',' ||
|
||||||
EXECUTE 'CREATE ROLE ' || authenticated_role || ' NOLOGIN NOINHERIT';
|
quote_ident(service_role) || ' TO authenticator';
|
||||||
EXECUTE 'CREATE ROLE ' || service_role || ' NOLOGIN NOINHERIT bypassrls';
|
|
||||||
|
|
||||||
create user authenticator noinherit;
|
EXECUTE 'GRANT USAGE ON SCHEMA storage TO ' ||
|
||||||
EXECUTE 'grant ' || anon_role || ' to authenticator';
|
quote_ident(anon_role) || ',' ||
|
||||||
EXECUTE 'grant ' || authenticated_role || ' to authenticator';
|
quote_ident(authenticated_role) || ',' ||
|
||||||
EXECUTE 'grant ' || service_role || ' to authenticator';
|
quote_ident(service_role);
|
||||||
grant postgres to authenticator;
|
|
||||||
|
|
||||||
EXECUTE 'grant usage on schema storage to postgres,' || anon_role || ',' || authenticated_role || ',' || service_role;
|
EXECUTE 'ALTER DEFAULT PRIVILEGES IN SCHEMA storage GRANT ALL ON TABLES TO ' ||
|
||||||
|
quote_ident(anon_role) || ',' || quote_ident(authenticated_role) || ',' || quote_ident(service_role);
|
||||||
EXECUTE 'alter default privileges in schema storage grant all on tables to postgres,' || anon_role || ',' || authenticated_role || ',' || service_role;
|
EXECUTE 'ALTER DEFAULT PRIVILEGES IN SCHEMA storage GRANT ALL ON FUNCTIONS TO ' ||
|
||||||
EXECUTE 'alter default privileges in schema storage grant all on functions to postgres,' || anon_role || ',' || authenticated_role || ',' || service_role;
|
quote_ident(anon_role) || ',' || quote_ident(authenticated_role) || ',' || quote_ident(service_role);
|
||||||
EXECUTE 'alter default privileges in schema storage grant all on sequences to postgres,' || anon_role || ',' || authenticated_role || ',' || service_role;
|
EXECUTE 'ALTER DEFAULT PRIVILEGES IN SCHEMA storage GRANT ALL ON SEQUENCES TO ' ||
|
||||||
|
quote_ident(anon_role) || ',' || quote_ident(authenticated_role) || ',' || quote_ident(service_role);
|
||||||
END$$;
|
END$$;
|
||||||
|
|
||||||
|
-- ✅ 创建 migrations 表(幂等)
|
||||||
CREATE TABLE IF NOT EXISTS "storage"."migrations" (
|
CREATE TABLE IF NOT EXISTS storage.migrations (
|
||||||
id integer PRIMARY KEY,
|
id integer PRIMARY KEY,
|
||||||
name varchar(100) UNIQUE NOT NULL,
|
name varchar(100) UNIQUE NOT NULL,
|
||||||
hash varchar(40) NOT NULL, -- sha1 hex encoded hash of the file name and contents, to ensure it hasn't been altered since applying the migration
|
hash varchar(40) NOT NULL,
|
||||||
executed_at timestamp DEFAULT current_timestamp
|
executed_at timestamp DEFAULT current_timestamp
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS "storage"."buckets" (
|
-- ✅ 创建 buckets 表(不含 public 字段)
|
||||||
"id" text not NULL,
|
CREATE TABLE IF NOT EXISTS storage.buckets (
|
||||||
"name" text NOT NULL,
|
id text NOT NULL,
|
||||||
"owner" uuid,
|
name text NOT NULL,
|
||||||
"created_at" timestamptz DEFAULT now(),
|
owner uuid,
|
||||||
"updated_at" timestamptz DEFAULT now(),
|
created_at timestamptz DEFAULT now(),
|
||||||
PRIMARY KEY ("id")
|
updated_at timestamptz DEFAULT now(),
|
||||||
|
PRIMARY KEY (id)
|
||||||
);
|
);
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS "bname" ON "storage"."buckets" USING BTREE ("name");
|
CREATE UNIQUE INDEX IF NOT EXISTS bname ON storage.buckets (name);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS "storage"."objects" (
|
-- ✅ 补 public 字段(兼容升级)
|
||||||
"id" uuid NOT NULL DEFAULT gen_random_uuid(),
|
DO $$
|
||||||
"bucket_id" text,
|
BEGIN
|
||||||
"name" text,
|
IF NOT EXISTS (
|
||||||
"owner" uuid,
|
SELECT 1 FROM information_schema.columns
|
||||||
"created_at" timestamptz DEFAULT now(),
|
WHERE table_schema = 'storage'
|
||||||
"updated_at" timestamptz DEFAULT now(),
|
AND table_name = 'buckets'
|
||||||
"last_accessed_at" timestamptz DEFAULT now(),
|
AND column_name = 'public'
|
||||||
"metadata" jsonb,
|
) THEN
|
||||||
CONSTRAINT "objects_bucketId_fkey" FOREIGN KEY ("bucket_id") REFERENCES "storage"."buckets"("id"),
|
ALTER TABLE storage.buckets
|
||||||
PRIMARY KEY ("id")
|
ADD COLUMN public boolean NOT NULL DEFAULT false;
|
||||||
|
END IF;
|
||||||
|
END$$;
|
||||||
|
|
||||||
|
-- ✅ 创建 objects 表
|
||||||
|
CREATE TABLE IF NOT EXISTS storage.objects (
|
||||||
|
id uuid NOT NULL DEFAULT gen_random_uuid(),
|
||||||
|
bucket_id text,
|
||||||
|
name text,
|
||||||
|
owner uuid,
|
||||||
|
created_at timestamptz DEFAULT now(),
|
||||||
|
updated_at timestamptz DEFAULT now(),
|
||||||
|
last_accessed_at timestamptz DEFAULT now(),
|
||||||
|
metadata jsonb,
|
||||||
|
CONSTRAINT objects_bucketId_fkey FOREIGN KEY (bucket_id) REFERENCES storage.buckets(id),
|
||||||
|
PRIMARY KEY (id)
|
||||||
);
|
);
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS "bucketid_objname" ON "storage"."objects" USING BTREE ("bucket_id","name");
|
CREATE UNIQUE INDEX IF NOT EXISTS bucketid_objname ON storage.objects (bucket_id, name);
|
||||||
CREATE INDEX IF NOT EXISTS name_prefix_search ON storage.objects(name text_pattern_ops);
|
CREATE INDEX IF NOT EXISTS name_prefix_search ON storage.objects (name text_pattern_ops);
|
||||||
|
|
||||||
|
-- ✅ 启用 RLS
|
||||||
ALTER TABLE storage.objects ENABLE ROW LEVEL SECURITY;
|
ALTER TABLE storage.objects ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
drop function if exists storage.foldername;
|
-- ✅ 内置函数(可重用)
|
||||||
CREATE OR REPLACE FUNCTION storage.foldername(name text)
|
CREATE OR REPLACE FUNCTION storage.foldername(name text)
|
||||||
RETURNS text[]
|
RETURNS text[] LANGUAGE plpgsql AS $$
|
||||||
LANGUAGE plpgsql
|
DECLARE _parts text[];
|
||||||
AS $function$
|
|
||||||
DECLARE
|
|
||||||
_parts text[];
|
|
||||||
BEGIN
|
BEGIN
|
||||||
select string_to_array(name, '/') into _parts;
|
SELECT string_to_array(name, '/') INTO _parts;
|
||||||
return _parts[1:array_length(_parts,1)-1];
|
RETURN _parts[1:array_length(_parts,1)-1];
|
||||||
END
|
END$$;
|
||||||
$function$;
|
|
||||||
|
|
||||||
drop function if exists storage.filename;
|
|
||||||
CREATE OR REPLACE FUNCTION storage.filename(name text)
|
CREATE OR REPLACE FUNCTION storage.filename(name text)
|
||||||
RETURNS text
|
RETURNS text LANGUAGE plpgsql AS $$
|
||||||
LANGUAGE plpgsql
|
DECLARE _parts text[];
|
||||||
AS $function$
|
|
||||||
DECLARE
|
|
||||||
_parts text[];
|
|
||||||
BEGIN
|
BEGIN
|
||||||
select string_to_array(name, '/') into _parts;
|
SELECT string_to_array(name, '/') INTO _parts;
|
||||||
return _parts[array_length(_parts,1)];
|
RETURN _parts[array_length(_parts,1)];
|
||||||
END
|
END$$;
|
||||||
$function$;
|
|
||||||
|
|
||||||
drop function if exists storage.extension;
|
|
||||||
CREATE OR REPLACE FUNCTION storage.extension(name text)
|
CREATE OR REPLACE FUNCTION storage.extension(name text)
|
||||||
RETURNS text
|
RETURNS text LANGUAGE plpgsql AS $$
|
||||||
LANGUAGE plpgsql
|
DECLARE _parts text[]; _filename text;
|
||||||
AS $function$
|
|
||||||
DECLARE
|
|
||||||
_parts text[];
|
|
||||||
_filename text;
|
|
||||||
BEGIN
|
BEGIN
|
||||||
select string_to_array(name, '/') into _parts;
|
SELECT string_to_array(name, '/') INTO _parts;
|
||||||
select _parts[array_length(_parts,1)] into _filename;
|
SELECT _parts[array_length(_parts,1)] INTO _filename;
|
||||||
-- @todo return the last part instead of 2
|
RETURN reverse(split_part(reverse(_filename), '.', 1));
|
||||||
return reverse(split_part(reverse(_filename), '.', 1));
|
END$$;
|
||||||
END
|
|
||||||
$function$;
|
|
||||||
|
|
||||||
-- @todo can this query be optimised further?
|
-- ✅ 文件夹搜索函数(用于前端分层目录浏览)
|
||||||
drop function if exists storage.search;
|
CREATE OR REPLACE FUNCTION storage.search(
|
||||||
CREATE OR REPLACE FUNCTION storage.search(prefix text, bucketname text, limits int DEFAULT 100, levels int DEFAULT 1, offsets int DEFAULT 0)
|
prefix text, bucketname text, limits int DEFAULT 100, levels int DEFAULT 1, offsets int DEFAULT 0
|
||||||
|
)
|
||||||
RETURNS TABLE (
|
RETURNS TABLE (
|
||||||
name text,
|
name text,
|
||||||
id uuid,
|
id uuid,
|
||||||
updated_at TIMESTAMPTZ,
|
updated_at timestamptz,
|
||||||
created_at TIMESTAMPTZ,
|
created_at timestamptz,
|
||||||
last_accessed_at TIMESTAMPTZ,
|
last_accessed_at timestamptz,
|
||||||
metadata jsonb
|
metadata jsonb
|
||||||
)
|
)
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql AS $$
|
||||||
AS $function$
|
|
||||||
BEGIN
|
BEGIN
|
||||||
return query
|
RETURN QUERY
|
||||||
with files_folders as (
|
WITH files_folders AS (
|
||||||
select ((string_to_array(objects.name, '/'))[levels]) as folder
|
SELECT (string_to_array(objects.name, '/'))[levels] AS folder
|
||||||
from objects
|
FROM storage.objects
|
||||||
where objects.name ilike prefix || '%'
|
WHERE objects.name ILIKE prefix || '%'
|
||||||
and bucket_id = bucketname
|
AND bucket_id = bucketname
|
||||||
GROUP by folder
|
GROUP BY folder
|
||||||
limit limits
|
LIMIT limits OFFSET offsets
|
||||||
offset offsets
|
)
|
||||||
)
|
SELECT
|
||||||
select files_folders.folder as name, objects.id, objects.updated_at, objects.created_at, objects.last_accessed_at, objects.metadata from files_folders
|
files_folders.folder AS name,
|
||||||
left join objects
|
objects.id,
|
||||||
on prefix || files_folders.folder = objects.name and objects.bucket_id=bucketname;
|
objects.updated_at,
|
||||||
END
|
objects.created_at,
|
||||||
$function$;
|
objects.last_accessed_at,
|
||||||
|
objects.metadata
|
||||||
|
FROM files_folders
|
||||||
|
LEFT JOIN storage.objects
|
||||||
|
ON prefix || files_folders.folder = objects.name
|
||||||
|
AND objects.bucket_id = bucketname;
|
||||||
|
END$$;
|
||||||
|
|
||||||
|
-- ✅ 管理员角色创建(幂等)
|
||||||
DO $$
|
DO $$
|
||||||
DECLARE
|
DECLARE
|
||||||
install_roles text = COALESCE(current_setting('storage.install_roles', true), 'true');
|
install_roles text := COALESCE(current_setting('storage.install_roles', true), 'true');
|
||||||
super_user text = COALESCE(current_setting('storage.super_user', true), 'supabase_storage_admin');
|
super_user text := COALESCE(current_setting('storage.super_user', true), 'supabase_storage_admin');
|
||||||
BEGIN
|
BEGIN
|
||||||
IF install_roles != 'true' THEN
|
IF install_roles != 'true' THEN RETURN; END IF;
|
||||||
RETURN;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = super_user) THEN
|
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = super_user) THEN
|
||||||
EXECUTE 'CREATE USER ' || super_user || ' NOINHERIT CREATEROLE LOGIN NOREPLICATION';
|
EXECUTE 'CREATE ROLE ' || quote_ident(super_user) || ' NOINHERIT CREATEROLE LOGIN NOREPLICATION';
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- Grant privileges to Super User
|
EXECUTE 'GRANT ALL PRIVILEGES ON SCHEMA storage TO ' || quote_ident(super_user);
|
||||||
EXECUTE 'GRANT ALL PRIVILEGES ON SCHEMA storage TO ' || super_user;
|
EXECUTE 'GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA storage TO ' || quote_ident(super_user);
|
||||||
EXECUTE 'GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA storage TO ' || super_user;
|
EXECUTE 'GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA storage TO ' || quote_ident(super_user);
|
||||||
EXECUTE 'GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA storage TO ' || super_user;
|
|
||||||
|
|
||||||
IF super_user != 'postgres' THEN
|
IF super_user != 'postgres' THEN
|
||||||
EXECUTE 'ALTER USER ' || super_user || ' SET search_path = "storage"';
|
EXECUTE 'ALTER ROLE ' || quote_ident(super_user) || ' SET search_path = "storage"';
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
EXECUTE 'ALTER table "storage".objects owner to ' || super_user;
|
EXECUTE 'ALTER TABLE storage.objects OWNER TO ' || quote_ident(super_user);
|
||||||
EXECUTE 'ALTER table "storage".buckets owner to ' || super_user;
|
EXECUTE 'ALTER TABLE storage.buckets OWNER TO ' || quote_ident(super_user);
|
||||||
EXECUTE 'ALTER table "storage".migrations OWNER TO ' || super_user;
|
EXECUTE 'ALTER TABLE storage.migrations OWNER TO ' || quote_ident(super_user);
|
||||||
EXECUTE 'ALTER function "storage".foldername(text) owner to ' || super_user;
|
|
||||||
EXECUTE 'ALTER function "storage".filename(text) owner to ' || super_user;
|
EXECUTE 'ALTER FUNCTION storage.foldername(text) OWNER TO ' || quote_ident(super_user);
|
||||||
EXECUTE 'ALTER function "storage".extension(text) owner to ' || super_user;
|
EXECUTE 'ALTER FUNCTION storage.filename(text) OWNER TO ' || quote_ident(super_user);
|
||||||
EXECUTE 'ALTER function "storage".search(text,text,int,int,int) owner to ' || super_user;
|
EXECUTE 'ALTER FUNCTION storage.extension(text) OWNER TO ' || quote_ident(super_user);
|
||||||
|
EXECUTE 'ALTER FUNCTION storage.search(text,text,int,int,int) OWNER TO ' || quote_ident(super_user);
|
||||||
|
END$$;
|
||||||
|
|
||||||
|
-- ✅ 插入默认 bucket(公开访问,用于头像)
|
||||||
|
INSERT INTO storage.buckets (id, name, owner, public)
|
||||||
|
VALUES ('profile_images', 'profile_images', NULL, true)
|
||||||
|
ON CONFLICT (id) DO UPDATE SET public = true;
|
||||||
|
|
||||||
|
-- ✅ RLS 策略定义(幂等)
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM pg_policies WHERE policyname = 'anon upload profile_images'
|
||||||
|
) THEN
|
||||||
|
EXECUTE 'CREATE POLICY "anon upload profile_images"
|
||||||
|
ON storage.objects
|
||||||
|
FOR INSERT
|
||||||
|
TO anon
|
||||||
|
WITH CHECK (bucket_id = ''profile_images'')';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM pg_policies WHERE policyname = 'anon read profile_images'
|
||||||
|
) THEN
|
||||||
|
EXECUTE 'CREATE POLICY "anon read profile_images"
|
||||||
|
ON storage.objects
|
||||||
|
FOR SELECT
|
||||||
|
TO anon
|
||||||
|
USING (bucket_id = ''profile_images'')';
|
||||||
|
END IF;
|
||||||
|
END$$;
|
||||||
|
|
||||||
|
|
||||||
|
-- ✅ RLS 策略定义(files bucket,幂等)
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM pg_policies WHERE policyname = 'anon upload files'
|
||||||
|
) THEN
|
||||||
|
EXECUTE 'CREATE POLICY "anon upload files"
|
||||||
|
ON storage.objects
|
||||||
|
FOR INSERT
|
||||||
|
TO anon
|
||||||
|
WITH CHECK (bucket_id = ''files'')';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM pg_policies WHERE policyname = 'anon read files'
|
||||||
|
) THEN
|
||||||
|
EXECUTE 'CREATE POLICY "anon read files"
|
||||||
|
ON storage.objects
|
||||||
|
FOR SELECT
|
||||||
|
TO anon
|
||||||
|
USING (bucket_id = ''files'')';
|
||||||
|
END IF;
|
||||||
END$$;
|
END$$;
|
||||||
|
|
|
||||||
|
|
@ -17,22 +17,27 @@ serverurl=unix:///var/run/supervisor.sock
|
||||||
|
|
||||||
[program:postgres]
|
[program:postgres]
|
||||||
command=/bin/bash /supabase/postgres/wrapper.sh
|
command=/bin/bash /supabase/postgres/wrapper.sh
|
||||||
|
priority=10
|
||||||
user=root
|
user=root
|
||||||
autorestart=true
|
autorestart=true
|
||||||
stdout_logfile=/var/log/postgres.out.log
|
stdout_logfile=/var/log/postgres.out.log
|
||||||
stderr_logfile=/var/log/postgres.err.log
|
stderr_logfile=/var/log/postgres.err.log
|
||||||
environment=POSTGRES_PASSWORD="postgres"
|
environment=POSTGRES_PASSWORD="postgres"
|
||||||
|
|
||||||
|
|
||||||
[program:postgrest]
|
[program:postgrest]
|
||||||
command=/bin/bash /supabase/postgrest/wrapper.sh
|
command=/bin/bash /supabase/postgrest/wrapper.sh
|
||||||
|
priority=20
|
||||||
autorestart=true
|
autorestart=true
|
||||||
user=root
|
user=root
|
||||||
stderr_logfile=/var/log/postgrest.err.log
|
stderr_logfile=/var/log/postgrest.err.log
|
||||||
stdout_logfile=/var/log/postgrest.out.log
|
stdout_logfile=/var/log/postgrest.out.log
|
||||||
environment=PGRST_DB_URI="postgres://postgres:postgres@localhost:5432/postgres",PGRST_DB_ANON_ROLE="anon",PGRST_DB_SCHEMA="public",PGRST_JWT_SECRET="super-secret-jwt-token-with-at-least-32-characters-long",PGRST_SERVER_PORT="3000",PGRST_LOG_LEVEL="debug"
|
environment=PGRST_DB_URI="postgres://postgres:postgres@localhost:5432/postgres",PGRST_DB_ANON_ROLE="anon",PGRST_DB_SCHEMA="public",PGRST_JWT_SECRET="super-secret-jwt-token-with-at-least-32-characters-long",PGRST_SERVER_PORT="3000",PGRST_LOG_LEVEL="debug"
|
||||||
|
|
||||||
|
|
||||||
[program:auth]
|
[program:auth]
|
||||||
command=/bin/bash /supabase/gotrue/wrapper.sh
|
command=/bin/bash /supabase/gotrue/wrapper.sh
|
||||||
|
priority=40
|
||||||
autorestart=true
|
autorestart=true
|
||||||
user=1000
|
user=1000
|
||||||
stderr_logfile=/var/log/auth.err.log
|
stderr_logfile=/var/log/auth.err.log
|
||||||
|
|
@ -41,12 +46,16 @@ stdout_logfile=/var/log/auth.out.log
|
||||||
[program:storage-api]
|
[program:storage-api]
|
||||||
directory=/supabase/storage-api
|
directory=/supabase/storage-api
|
||||||
command=/bin/bash /supabase/storage-api/wrapper.sh
|
command=/bin/bash /supabase/storage-api/wrapper.sh
|
||||||
|
priority=50
|
||||||
autorestart=true
|
autorestart=true
|
||||||
stderr_logfile=/var/log/storage-api.err.log
|
stderr_logfile=/var/log/storage-api.err.log
|
||||||
stdout_logfile=/var/log/storage-api.out.log
|
stdout_logfile=/var/log/storage-api.out.log
|
||||||
|
environment=MULTI_TENANT="false"
|
||||||
|
|
||||||
|
|
||||||
[program:kong]
|
[program:kong]
|
||||||
command=/bin/bash /supabase/kong/wrapper.sh
|
command=/bin/bash /supabase/kong/wrapper.sh
|
||||||
|
priority=30
|
||||||
user=root
|
user=root
|
||||||
autorestart=true
|
autorestart=true
|
||||||
redirect_stderr=true
|
redirect_stderr=true
|
||||||
|
|
@ -55,6 +64,7 @@ stdout_logfile=auto
|
||||||
|
|
||||||
[program:chatdesk-ui]
|
[program:chatdesk-ui]
|
||||||
command=/bin/bash /supabase/chatdesk/wrapper.sh
|
command=/bin/bash /supabase/chatdesk/wrapper.sh
|
||||||
|
priority=60
|
||||||
user=root
|
user=root
|
||||||
autorestart=true
|
autorestart=true
|
||||||
stderr_logfile=/var/log/chatdesk-ui.err.log
|
stderr_logfile=/var/log/chatdesk-ui.err.log
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,24 @@ export SERVER_PORT="5000"
|
||||||
export AUTH_JWT_SECRET="super-secret-jwt-token-with-at-least-32-characters-long"
|
export AUTH_JWT_SECRET="super-secret-jwt-token-with-at-least-32-characters-long"
|
||||||
export AUTH_JWT_ALGORITHM="HS256"
|
export AUTH_JWT_ALGORITHM="HS256"
|
||||||
export DATABASE_URL="postgres://supabase_admin:postgres@127.0.0.1:5432/postgres"
|
export DATABASE_URL="postgres://supabase_admin:postgres@127.0.0.1:5432/postgres"
|
||||||
export DATABASE_POOL_URL="postgresql://postgres:postgres@127.0.0.1:6432/postgres"
|
#export DATABASE_POOL_URL="postgresql://postgres:postgres@127.0.0.1:6432/postgres"
|
||||||
#export DB_INSTALL_ROLES="true"
|
#export DB_INSTALL_ROLES="true"
|
||||||
export STORAGE_BACKEND="file"
|
export STORAGE_BACKEND="file"
|
||||||
|
export FILE_SIZE_LIMIT=52428800 # 50 * 1024 * 1024
|
||||||
export FILE_STORAGE_BACKEND_PATH="/var/lib/storage"
|
export FILE_STORAGE_BACKEND_PATH="/var/lib/storage"
|
||||||
export DB_INSTALL_ROLES="false"
|
export DB_INSTALL_ROLES="true"
|
||||||
|
|
||||||
|
# ✅ 明确告诉系统是单租户,避免 undefined
|
||||||
|
export MULTI_TENANT="false"
|
||||||
|
export TENANT_ID="storage-single-tenant"
|
||||||
|
export REGION="chatdesk"
|
||||||
|
export SUPABASE_PROJECT_REF="storage-single-tenant"
|
||||||
|
|
||||||
|
# ✅ 日志调试(可选)
|
||||||
|
echo "[DEBUG] MULTI_TENANT=$MULTI_TENANT"
|
||||||
|
echo "[DEBUG] TENANT_ID=$TENANT_ID"
|
||||||
|
echo "[DEBUG] FILE_STORAGE_BACKEND_PATH=$FILE_STORAGE_BACKEND_PATH"
|
||||||
|
|
||||||
|
|
||||||
# 等待 gotrue 服务可用(健康检查)
|
# 等待 gotrue 服务可用(健康检查)
|
||||||
echo "[storage-api] Waiting for GoTrue at /user..."
|
echo "[storage-api] Waiting for GoTrue at /user..."
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue