Merge branch 'fix/thread-rerender-issue' of https://github.com/menloresearch/jan into fix/thread-rerender-issue
This commit is contained in:
commit
c5c39f22e6
@ -47,23 +47,23 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => {
|
|||||||
const [isFocused, setIsFocused] = useState(false)
|
const [isFocused, setIsFocused] = useState(false)
|
||||||
const [rows, setRows] = useState(1)
|
const [rows, setRows] = useState(1)
|
||||||
const serviceHub = useServiceHub()
|
const serviceHub = useServiceHub()
|
||||||
const {
|
const streamingContent = useAppState((state) => state.streamingContent)
|
||||||
streamingContent,
|
const abortControllers = useAppState((state) => state.abortControllers)
|
||||||
abortControllers,
|
const loadingModel = useAppState((state) => state.loadingModel)
|
||||||
loadingModel,
|
const tools = useAppState((state) => state.tools)
|
||||||
tools,
|
const cancelToolCall = useAppState((state) => state.cancelToolCall)
|
||||||
cancelToolCall,
|
const prompt = usePrompt((state) => state.prompt)
|
||||||
} = useAppState()
|
const setPrompt = usePrompt((state) => state.setPrompt)
|
||||||
const { prompt, setPrompt } = usePrompt()
|
const currentThreadId = useThreads((state) => state.currentThreadId)
|
||||||
const { currentThreadId } = useThreads()
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { spellCheckChatInput } = useGeneralSetting()
|
const { spellCheckChatInput } = useGeneralSetting()
|
||||||
useTools()
|
useTools()
|
||||||
|
|
||||||
const maxRows = 10
|
const maxRows = 10
|
||||||
|
|
||||||
const { selectedModel, selectedProvider } = useModelProvider()
|
const selectedModel = useModelProvider((state) => state.selectedModel)
|
||||||
const { sendMessage } = useChat()
|
const selectedProvider = useModelProvider((state) => state.selectedProvider)
|
||||||
|
const sendMessage = useChat()
|
||||||
const [message, setMessage] = useState('')
|
const [message, setMessage] = useState('')
|
||||||
const [dropdownToolsAvailable, setDropdownToolsAvailable] = useState(false)
|
const [dropdownToolsAvailable, setDropdownToolsAvailable] = useState(false)
|
||||||
const [tooltipToolsAvailable, setTooltipToolsAvailable] = useState(false)
|
const [tooltipToolsAvailable, setTooltipToolsAvailable] = useState(false)
|
||||||
|
|||||||
@ -12,7 +12,7 @@ export const GenerateResponseButton = ({ threadId }: { threadId: string }) => {
|
|||||||
messages: state.messages[threadId],
|
messages: state.messages[threadId],
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
const { sendMessage } = useChat()
|
const sendMessage = useChat()
|
||||||
const generateAIResponse = () => {
|
const generateAIResponse = () => {
|
||||||
const latestUserMessage = messages[messages.length - 1]
|
const latestUserMessage = messages[messages.length - 1]
|
||||||
if (
|
if (
|
||||||
|
|||||||
@ -21,7 +21,7 @@ function extractReasoningSegment(text: string) {
|
|||||||
// Use memo with no dependencies to allow re-renders when props change
|
// Use memo with no dependencies to allow re-renders when props change
|
||||||
// Avoid duplicate reasoning segments after tool calls
|
// Avoid duplicate reasoning segments after tool calls
|
||||||
export const StreamingContent = memo(({ threadId }: Props) => {
|
export const StreamingContent = memo(({ threadId }: Props) => {
|
||||||
const { streamingContent } = useAppState()
|
const streamingContent = useAppState((state) => state.streamingContent)
|
||||||
const { getMessages } = useMessages()
|
const { getMessages } = useMessages()
|
||||||
const messages = getMessages(threadId)
|
const messages = getMessages(threadId)
|
||||||
|
|
||||||
@ -68,6 +68,7 @@ export const StreamingContent = memo(({ threadId }: Props) => {
|
|||||||
}}
|
}}
|
||||||
{...streamingContent}
|
{...streamingContent}
|
||||||
isLastMessage={true}
|
isLastMessage={true}
|
||||||
|
streamingThread={streamingContent.thread_id}
|
||||||
showAssistant={
|
showAssistant={
|
||||||
messages.length > 0
|
messages.length > 0
|
||||||
? messages[messages.length - 1].role !== 'assistant'
|
? messages[messages.length - 1].role !== 'assistant'
|
||||||
|
|||||||
@ -27,12 +27,15 @@ const useThinkingStore = create<ThinkingBlockState>((set) => ({
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
const ThinkingBlock = ({ id, text }: Props) => {
|
const ThinkingBlock = ({ id, text }: Props) => {
|
||||||
const { thinkingState, setThinkingState } = useThinkingStore()
|
const thinkingState = useThinkingStore((state) => state.thinkingState)
|
||||||
|
const setThinkingState = useThinkingStore((state) => state.setThinkingState)
|
||||||
const isStreaming = useAppState((state) => !!state.streamingContent)
|
const isStreaming = useAppState((state) => !!state.streamingContent)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
// Check for thinking formats
|
// Check for thinking formats
|
||||||
const hasThinkTag = text.includes('<think>') && !text.includes('</think>')
|
const hasThinkTag = text.includes('<think>') && !text.includes('</think>')
|
||||||
const hasAnalysisChannel = text.includes('<|channel|>analysis<|message|>') && !text.includes('<|start|>assistant<|channel|>final<|message|>')
|
const hasAnalysisChannel =
|
||||||
|
text.includes('<|channel|>analysis<|message|>') &&
|
||||||
|
!text.includes('<|start|>assistant<|channel|>final<|message|>')
|
||||||
const loading = (hasThinkTag || hasAnalysisChannel) && isStreaming
|
const loading = (hasThinkTag || hasAnalysisChannel) && isStreaming
|
||||||
const isExpanded = thinkingState[id] ?? (loading ? true : false)
|
const isExpanded = thinkingState[id] ?? (loading ? true : false)
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
|
|||||||
@ -68,6 +68,7 @@ export const ThreadContent = memo(
|
|||||||
isLastMessage?: boolean
|
isLastMessage?: boolean
|
||||||
index?: number
|
index?: number
|
||||||
showAssistant?: boolean
|
showAssistant?: boolean
|
||||||
|
streamingThread?: string
|
||||||
|
|
||||||
streamTools?: any
|
streamTools?: any
|
||||||
contextOverflowModal?: React.ReactNode | null
|
contextOverflowModal?: React.ReactNode | null
|
||||||
@ -75,7 +76,7 @@ export const ThreadContent = memo(
|
|||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { selectedModel } = useModelProvider()
|
const selectedModel = useModelProvider((state) => state.selectedModel)
|
||||||
|
|
||||||
// Use useMemo to stabilize the components prop
|
// Use useMemo to stabilize the components prop
|
||||||
const linkComponents = useMemo(
|
const linkComponents = useMemo(
|
||||||
@ -132,8 +133,9 @@ export const ThreadContent = memo(
|
|||||||
return { reasoningSegment: undefined, textSegment: text }
|
return { reasoningSegment: undefined, textSegment: text }
|
||||||
}, [text])
|
}, [text])
|
||||||
|
|
||||||
const { getMessages, deleteMessage } = useMessages()
|
const getMessages = useMessages((state) => state.getMessages)
|
||||||
const { sendMessage } = useChat()
|
const deleteMessage = useMessages((state) => state.deleteMessage)
|
||||||
|
const sendMessage = useChat()
|
||||||
|
|
||||||
const regenerate = useCallback(() => {
|
const regenerate = useCallback(() => {
|
||||||
// Only regenerate assistant message is allowed
|
// Only regenerate assistant message is allowed
|
||||||
|
|||||||
@ -46,14 +46,16 @@ const SortableItem = memo(({ thread }: { thread: Thread }) => {
|
|||||||
} = useSortable({ id: thread.id, disabled: true })
|
} = useSortable({ id: thread.id, disabled: true })
|
||||||
|
|
||||||
const isSmallScreen = useSmallScreen()
|
const isSmallScreen = useSmallScreen()
|
||||||
const { setLeftPanel } = useLeftPanel()
|
const setLeftPanel = useLeftPanel(state => state.setLeftPanel)
|
||||||
|
|
||||||
const style = {
|
const style = {
|
||||||
transform: CSS.Transform.toString(transform),
|
transform: CSS.Transform.toString(transform),
|
||||||
transition,
|
transition,
|
||||||
opacity: isDragging ? 0.5 : 1,
|
opacity: isDragging ? 0.5 : 1,
|
||||||
}
|
}
|
||||||
const { toggleFavorite, deleteThread, renameThread } = useThreads()
|
const toggleFavorite = useThreads((state) => state.toggleFavorite)
|
||||||
|
const deleteThread = useThreads((state) => state.deleteThread)
|
||||||
|
const renameThread = useThreads((state) => state.renameThread)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [openDropdown, setOpenDropdown] = useState(false)
|
const [openDropdown, setOpenDropdown] = useState(false)
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { memo } from 'react'
|
|||||||
import { useAppState } from '@/hooks/useAppState'
|
import { useAppState } from '@/hooks/useAppState'
|
||||||
import { toNumber } from '@/utils/number'
|
import { toNumber } from '@/utils/number'
|
||||||
import { Gauge } from 'lucide-react'
|
import { Gauge } from 'lucide-react'
|
||||||
|
import { memo } from 'react'
|
||||||
|
|
||||||
interface TokenSpeedIndicatorProps {
|
interface TokenSpeedIndicatorProps {
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown>
|
||||||
@ -41,4 +42,4 @@ export const TokenSpeedIndicator = memo(({
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
export default TokenSpeedIndicator
|
export default memo(TokenSpeedIndicator)
|
||||||
|
|||||||
@ -16,7 +16,8 @@ import { useAppState } from '@/hooks/useAppState'
|
|||||||
|
|
||||||
export default function ErrorDialog() {
|
export default function ErrorDialog() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { errorMessage, setErrorMessage } = useAppState()
|
const errorMessage = useAppState((state) => state.errorMessage)
|
||||||
|
const setErrorMessage = useAppState((state) => state.setErrorMessage)
|
||||||
const [isCopying, setIsCopying] = useState(false)
|
const [isCopying, setIsCopying] = useState(false)
|
||||||
const [isDetailExpanded, setIsDetailExpanded] = useState(true)
|
const [isDetailExpanded, setIsDetailExpanded] = useState(true)
|
||||||
|
|
||||||
|
|||||||
@ -82,7 +82,7 @@ export const useChat = () => {
|
|||||||
const selectedAssistant =
|
const selectedAssistant =
|
||||||
assistants.find((a) => a.id === currentAssistant?.id) || assistants[0]
|
assistants.find((a) => a.id === currentAssistant?.id) || assistants[0]
|
||||||
|
|
||||||
const getCurrentThread = useCallback(async () => {
|
const getCurrentThread = useCallback(async (prompt: string) => {
|
||||||
let currentThread = retrieveThread()
|
let currentThread = retrieveThread()
|
||||||
|
|
||||||
if (!currentThread) {
|
if (!currentThread) {
|
||||||
@ -225,7 +225,7 @@ export const useChat = () => {
|
|||||||
dataUrl: string
|
dataUrl: string
|
||||||
}>
|
}>
|
||||||
) => {
|
) => {
|
||||||
const activeThread = await getCurrentThread()
|
const activeThread = await getCurrentThread(message)
|
||||||
|
|
||||||
resetTokenSpeed()
|
resetTokenSpeed()
|
||||||
let activeProvider = currentProviderId
|
let activeProvider = currentProviderId
|
||||||
@ -570,5 +570,5 @@ export const useChat = () => {
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
return useMemo(() => ({ sendMessage }), [sendMessage])
|
return useMemo(() => (sendMessage), [sendMessage])
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,13 +38,18 @@ export function DataProvider() {
|
|||||||
verboseLogs,
|
verboseLogs,
|
||||||
proxyTimeout,
|
proxyTimeout,
|
||||||
} = useLocalApiServer()
|
} = useLocalApiServer()
|
||||||
const { setServerStatus } = useAppState()
|
const setServerStatus = useAppState((state) => state.setServerStatus)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('Initializing DataProvider...')
|
console.log('Initializing DataProvider...')
|
||||||
serviceHub.providers().getProviders().then(setProviders)
|
serviceHub.providers().getProviders().then(setProviders)
|
||||||
serviceHub.mcp().getMCPConfig().then((data) => setServers(data.mcpServers ?? {}))
|
serviceHub
|
||||||
serviceHub.assistants().getAssistants()
|
.mcp()
|
||||||
|
.getMCPConfig()
|
||||||
|
.then((data) => setServers(data.mcpServers ?? {}))
|
||||||
|
serviceHub
|
||||||
|
.assistants()
|
||||||
|
.getAssistants()
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
// Only update assistants if we have valid data
|
// Only update assistants if we have valid data
|
||||||
if (data && Array.isArray(data) && data.length > 0) {
|
if (data && Array.isArray(data) && data.length > 0) {
|
||||||
@ -61,12 +66,16 @@ export function DataProvider() {
|
|||||||
}, [serviceHub])
|
}, [serviceHub])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
serviceHub.threads().fetchThreads().then((threads) => {
|
serviceHub
|
||||||
|
.threads()
|
||||||
|
.fetchThreads()
|
||||||
|
.then((threads) => {
|
||||||
setThreads(threads)
|
setThreads(threads)
|
||||||
threads.forEach((thread) =>
|
threads.forEach((thread) =>
|
||||||
serviceHub.messages().fetchMessages(thread.id).then((messages) =>
|
serviceHub
|
||||||
setMessages(thread.id, messages)
|
.messages()
|
||||||
)
|
.fetchMessages(thread.id)
|
||||||
|
.then((messages) => setMessages(thread.id, messages))
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}, [serviceHub, setThreads, setMessages])
|
}, [serviceHub, setThreads, setMessages])
|
||||||
@ -157,7 +166,9 @@ export function DataProvider() {
|
|||||||
setServerStatus('pending')
|
setServerStatus('pending')
|
||||||
|
|
||||||
// Start the model first
|
// Start the model first
|
||||||
serviceHub.models().startModel(modelToStart.provider, modelToStart.model)
|
serviceHub
|
||||||
|
.models()
|
||||||
|
.startModel(modelToStart.provider, modelToStart.model)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log(`Model ${modelToStart.model} started successfully`)
|
console.log(`Model ${modelToStart.model} started successfully`)
|
||||||
|
|
||||||
|
|||||||
@ -132,7 +132,7 @@ function MCPServersDesktop() {
|
|||||||
const [loadingServers, setLoadingServers] = useState<{
|
const [loadingServers, setLoadingServers] = useState<{
|
||||||
[key: string]: boolean
|
[key: string]: boolean
|
||||||
}>({})
|
}>({})
|
||||||
const { setErrorMessage } = useAppState()
|
const setErrorMessage = useAppState((state) => state.setErrorMessage)
|
||||||
|
|
||||||
const handleOpenDialog = (serverKey?: string) => {
|
const handleOpenDialog = (serverKey?: string) => {
|
||||||
if (serverKey) {
|
if (serverKey) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user