// TODO: Separate into multiple contexts, keeping simple for now "use client" import { ChatbotUIContext } from "@/context/context" import { usePathname } from "next/navigation" import { getProfileByUserId } from "@/db/profile" import { getWorkspaceImageFromStorage } from "@/db/storage/workspace-images" import { getWorkspacesByUserId } from "@/db/workspaces" import { convertBlobToBase64 } from "@/lib/blob-to-b64" import { fetchHostedModels, fetchOllamaModels, fetchOpenRouterModels } from "@/lib/models/fetch-models" import { supabase } from "@/lib/supabase/browser-client" import { Tables } from "@/supabase/types" import { ChatFile, ChatMessage, ChatSettings, LLM, MessageImage, OpenRouterLLM, WorkspaceImage } from "@/types" import { AssistantImage } from "@/types/images/assistant-image" import { VALID_ENV_KEYS } from "@/types/valid-keys" import { useRouter } from "next/navigation" import { FC, useEffect, useState } from "react" interface GlobalStateProps { children: React.ReactNode } export const GlobalState: FC = ({ children }) => { const router = useRouter() const pathname = usePathname() // 获取当前路径 // PROFILE STORE const [profile, setProfile] = useState | null>(null) // ITEMS STORE const [assistants, setAssistants] = useState[]>([]) const [collections, setCollections] = useState[]>([]) const [chats, setChats] = useState[]>([]) const [files, setFiles] = useState[]>([]) const [folders, setFolders] = useState[]>([]) const [models, setModels] = useState[]>([]) const [presets, setPresets] = useState[]>([]) const [prompts, setPrompts] = useState[]>([]) const [tools, setTools] = useState[]>([]) const [workspaces, setWorkspaces] = useState[]>([]) // MODELS STORE const [envKeyMap, setEnvKeyMap] = useState>({}) const [availableHostedModels, setAvailableHostedModels] = useState([]) const [availableLocalModels, setAvailableLocalModels] = useState([]) const [availableOpenRouterModels, setAvailableOpenRouterModels] = useState< OpenRouterLLM[] >([]) // WORKSPACE STORE const [selectedWorkspace, setSelectedWorkspace] = useState | null>(null) const [workspaceImages, setWorkspaceImages] = useState([]) // PRESET STORE const [selectedPreset, setSelectedPreset] = useState | null>(null) // ASSISTANT STORE const [selectedAssistant, setSelectedAssistant] = useState | null>(null) const [assistantImages, setAssistantImages] = useState([]) const [openaiAssistants, setOpenaiAssistants] = useState([]) // PASSIVE CHAT STORE const [userInput, setUserInput] = useState("") const [chatMessages, setChatMessages] = useState([]) const [chatSettings, setChatSettings] = useState({ model: "gpt-4-turbo-preview", prompt: "You are a helpful AI assistant.", temperature: 0.5, contextLength: 4000, includeProfileContext: true, includeWorkspaceInstructions: true, embeddingsProvider: "openai" }) const [selectedChat, setSelectedChat] = useState | null>(null) const [chatFileItems, setChatFileItems] = useState[]>([]) // ACTIVE CHAT STORE const [isGenerating, setIsGenerating] = useState(false) const [firstTokenReceived, setFirstTokenReceived] = useState(false) const [abortController, setAbortController] = useState(null) // CHAT INPUT COMMAND STORE const [isPromptPickerOpen, setIsPromptPickerOpen] = useState(false) const [slashCommand, setSlashCommand] = useState("") const [isFilePickerOpen, setIsFilePickerOpen] = useState(false) const [hashtagCommand, setHashtagCommand] = useState("") const [isToolPickerOpen, setIsToolPickerOpen] = useState(false) const [toolCommand, setToolCommand] = useState("") const [focusPrompt, setFocusPrompt] = useState(false) const [focusFile, setFocusFile] = useState(false) const [focusTool, setFocusTool] = useState(false) const [focusAssistant, setFocusAssistant] = useState(false) const [atCommand, setAtCommand] = useState("") const [isAssistantPickerOpen, setIsAssistantPickerOpen] = useState(false) // ATTACHMENTS STORE const [chatFiles, setChatFiles] = useState([]) const [chatImages, setChatImages] = useState([]) const [newMessageFiles, setNewMessageFiles] = useState([]) const [newMessageImages, setNewMessageImages] = useState([]) const [showFilesDisplay, setShowFilesDisplay] = useState(false) // RETIEVAL STORE const [useRetrieval, setUseRetrieval] = useState(true) const [sourceCount, setSourceCount] = useState(4) // TOOL STORE const [selectedTools, setSelectedTools] = useState[]>([]) const [toolInUse, setToolInUse] = useState("none") useEffect(() => { ;(async () => { const profile = await fetchStartingData() if (profile) { const hostedModelRes = await fetchHostedModels(profile) if (!hostedModelRes) return setEnvKeyMap(hostedModelRes.envKeyMap) setAvailableHostedModels(hostedModelRes.hostedModels) if ( profile["openrouter_api_key"] || hostedModelRes.envKeyMap["openrouter"] ) { const openRouterModels = await fetchOpenRouterModels() if (!openRouterModels) return setAvailableOpenRouterModels(openRouterModels) } } if (process.env.NEXT_PUBLIC_OLLAMA_URL) { const localModels = await fetchOllamaModels() if (!localModels) return setAvailableLocalModels(localModels) } })() }, []) const fetchStartingData = async () => { const session = (await supabase.auth.getSession()).data.session if (session) { const user = session.user const profile = await getProfileByUserId(user.id) setProfile(profile) if (!profile.has_onboarded) { // 提取当前路径中的 locale 部分 const locale = pathname.split("/")[1] || "en" // 获取路径中的 locale 部分,如果没有则默认为 "en" // 强制跳转到带有 locale 的 setup 页面 return router.push(`/${locale}/setup`) } const workspaces = await getWorkspacesByUserId(user.id) setWorkspaces(workspaces) for (const workspace of workspaces) { let workspaceImageUrl = "" if (workspace.image_path) { workspaceImageUrl = (await getWorkspaceImageFromStorage(workspace.image_path)) || "" } if (workspaceImageUrl) { const response = await fetch(workspaceImageUrl) const blob = await response.blob() const base64 = await convertBlobToBase64(blob) setWorkspaceImages(prev => [ ...prev, { workspaceId: workspace.id, path: workspace.image_path, base64: base64, url: workspaceImageUrl } ]) } } return profile } } return ( {children} ) }