diff --git a/app/client/platforms/google.ts b/app/client/platforms/google.ts index 53ff00aee..a7bce4fc2 100644 --- a/app/client/platforms/google.ts +++ b/app/client/platforms/google.ts @@ -29,7 +29,7 @@ import { RequestPayload } from "./openai"; import { fetch } from "@/app/utils/stream"; export class GeminiProApi implements LLMApi { - path(path: string): string { + path(path: string, shouldStream = false): string { const accessStore = useAccessStore.getState(); let baseUrl = ""; @@ -51,8 +51,10 @@ export class GeminiProApi implements LLMApi { console.log("[Proxy Endpoint] ", baseUrl, path); let chatPath = [baseUrl, path].join("/"); + if (shouldStream) { + chatPath += chatPath.includes("?") ? "&alt=sse" : "?alt=sse"; + } - chatPath += chatPath.includes("?") ? "&alt=sse" : "?alt=sse"; return chatPath; } extractMessage(res: any) { @@ -60,6 +62,7 @@ export class GeminiProApi implements LLMApi { return ( res?.candidates?.at(0)?.content?.parts.at(0)?.text || + res?.at(0)?.candidates?.at(0)?.content?.parts.at(0)?.text || res?.error?.message || "" ); @@ -166,7 +169,10 @@ export class GeminiProApi implements LLMApi { options.onController?.(controller); try { // https://github.com/google-gemini/cookbook/blob/main/quickstarts/rest/Streaming_REST.ipynb - const chatPath = this.path(Google.ChatPath(modelConfig.model)); + const chatPath = this.path( + Google.ChatPath(modelConfig.model), + shouldStream, + ); const chatPayload = { method: "POST", diff --git a/app/components/chat.tsx b/app/components/chat.tsx index ed51d926f..51fe74fe7 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -960,9 +960,24 @@ function _Chat() { (scrollRef.current.scrollTop + scrollRef.current.clientHeight), ) <= 1 : false; + const isAttachWithTop = useMemo(() => { + const lastMessage = scrollRef.current?.lastElementChild as HTMLElement; + // if scrolllRef is not ready or no message, return false + if (!scrollRef?.current || !lastMessage) return false; + const topDistance = + lastMessage!.getBoundingClientRect().top - + scrollRef.current.getBoundingClientRect().top; + // leave some space for user question + return topDistance < 100; + }, [scrollRef?.current?.scrollHeight]); + + const isTyping = userInput !== ""; + + // if user is typing, should auto scroll to bottom + // if user is not typing, should auto scroll to bottom only if already at bottom const { setAutoScroll, scrollDomToBottom } = useScrollToBottom( scrollRef, - isScrolledToBottom, + (isScrolledToBottom || isAttachWithTop) && !isTyping, ); const [hitBottom, setHitBottom] = useState(true); const isMobileScreen = useMobileScreen(); @@ -2071,6 +2086,6 @@ function _Chat() { export function Chat() { const chatStore = useChatStore(); - const sessionIndex = chatStore.currentSessionIndex; - return <_Chat key={sessionIndex}>; + const session = chatStore.currentSession(); + return <_Chat key={session.id}>; } diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index 8ea731123..ba85f0970 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -90,7 +90,11 @@ export function PreCode(props: { children: any }) { const refText = ref.current.querySelector("code")?.innerText; if (htmlDom) { setHtmlCode((htmlDom as HTMLElement).innerText); - } else if (refText?.startsWith(")/g, diff --git a/app/utils.ts b/app/utils.ts index 2dd80b8a3..1c359ef95 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -257,11 +257,11 @@ export function isVisionModel(model: string) { const excludeKeywords = ["claude-3-5-haiku-20241022"]; const visionKeywords = [ "vision", - "claude-3", - "gemini-1.5-pro", - "gemini-1.5-flash", "gpt-4o", - "gpt-4o-mini", + "claude-3", + "gemini-1.5", + "qwen-vl", + "qwen2-vl", ]; const isGpt4Turbo = model.includes("gpt-4-turbo") && !model.includes("preview");