repositories
loading repo index
repositories
loading repo index
repository
loading code, commits, and activity
public Clawd ADK gateway launch mirror
stars
latest
clone command
git clone gitlawb://did:key:z6Mkq5mY...iFZ5/my-project-publ...git clone gitlawb://did:key:z6Mkq5mY.../my-project-publ...2fa351d6docs: add automaton and perps launch sources16d ago| #1 | import { useState } from 'react'; |
| #2 | import { MemoryClient, Memory as Mem0Memory } from 'mem0ai'; |
| #3 | import { OpenAI } from 'openai'; |
| #4 | import { Message, Memory } from '@/types'; |
| #5 | import { WELCOME_MESSAGE, INVALID_CONFIG_MESSAGE, ERROR_MESSAGE, Provider } from '@/constants/messages'; |
| #6 | |
| #7 | interface UseChatProps { |
| #8 | user: string; |
| #9 | mem0ApiKey: string; |
| #10 | openaiApiKey: string; |
| #11 | provider: Provider; |
| #12 | } |
| #13 | |
| #14 | interface UseChatReturn { |
| #15 | messages: Message[]; |
| #16 | memories: Memory[]; |
| #17 | thinking: boolean; |
| #18 | sendMessage: (content: string, fileData?: { type: string; data: string | Buffer }) => Promise<void>; |
| #19 | } |
| #20 | |
| #21 | type MessageContent = string | { |
| #22 | type: 'image_url'; |
| #23 | image_url: { |
| #24 | url: string; |
| #25 | }; |
| #26 | }; |
| #27 | |
| #28 | interface PromptMessage { |
| #29 | role: string; |
| #30 | content: MessageContent; |
| #31 | } |
| #32 | |
| #33 | export const useChat = ({ user, mem0ApiKey, openaiApiKey }: UseChatProps): UseChatReturn => { |
| #34 | const [messages, setMessages] = useState<Message[]>([WELCOME_MESSAGE]); |
| #35 | const [memories, setMemories] = useState<Memory[]>(); |
| #36 | const [thinking, setThinking] = useState(false); |
| #37 | |
| #38 | const openai = new OpenAI({ apiKey: openaiApiKey, dangerouslyAllowBrowser: true}); |
| #39 | |
| #40 | const updateMemories = async (messages: PromptMessage[]) => { |
| #41 | const memoryClient = new MemoryClient({ apiKey: mem0ApiKey || '' }); |
| #42 | try { |
| #43 | await memoryClient.add(messages, { |
| #44 | user_id: user, |
| #45 | }); |
| #46 | |
| #47 | const response = await memoryClient.getAll({ |
| #48 | user_id: user, |
| #49 | }); |
| #50 | |
| #51 | const newMemories = response.map((memory: Mem0Memory) => ({ |
| #52 | id: memory.id || '', |
| #53 | content: memory.memory || '', |
| #54 | timestamp: String(memory.updated_at) || '', |
| #55 | tags: memory.categories || [], |
| #56 | })); |
| #57 | setMemories(newMemories); |
| #58 | } catch (error) { |
| #59 | console.error('Error in updateMemories:', error); |
| #60 | } |
| #61 | }; |
| #62 | |
| #63 | const formatMessagesForPrompt = (messages: Message[]): PromptMessage[] => { |
| #64 | return messages.map((message) => { |
| #65 | if (message.image) { |
| #66 | return { |
| #67 | role: message.sender, |
| #68 | content: { |
| #69 | type: 'image_url', |
| #70 | image_url: { |
| #71 | url: message.image |
| #72 | } |
| #73 | }, |
| #74 | }; |
| #75 | } |
| #76 | |
| #77 | return { |
| #78 | role: message.sender, |
| #79 | content: message.content, |
| #80 | }; |
| #81 | }); |
| #82 | }; |
| #83 | |
| #84 | const sendMessage = async (content: string, fileData?: { type: string; data: string | Buffer }) => { |
| #85 | if (!content.trim() && !fileData) return; |
| #86 | |
| #87 | const memoryClient = new MemoryClient({ apiKey: mem0ApiKey || '' }); |
| #88 | |
| #89 | if (!user) { |
| #90 | const newMessage: Message = { |
| #91 | id: Date.now().toString(), |
| #92 | content, |
| #93 | sender: 'user', |
| #94 | timestamp: new Date().toLocaleTimeString(), |
| #95 | }; |
| #96 | setMessages((prev) => [...prev, newMessage, INVALID_CONFIG_MESSAGE]); |
| #97 | return; |
| #98 | } |
| #99 | |
| #100 | const userMessage: Message = { |
| #101 | id: Date.now().toString(), |
| #102 | content, |
| #103 | sender: 'user', |
| #104 | timestamp: new Date().toLocaleTimeString(), |
| #105 | ...(fileData?.type.startsWith('image/') && { image: fileData.data.toString() }), |
| #106 | }; |
| #107 | |
| #108 | setMessages((prev) => [...prev, userMessage]); |
| #109 | setThinking(true); |
| #110 | |
| #111 | // Get all messages for memory update |
| #112 | const allMessagesForMemory = formatMessagesForPrompt([...messages, userMessage]); |
| #113 | await updateMemories(allMessagesForMemory); |
| #114 | |
| #115 | try { |
| #116 | // Get only the last assistant message (if exists) and the current user message |
| #117 | const lastAssistantMessage = messages.filter(msg => msg.sender === 'assistant').slice(-1)[0]; |
| #118 | let messagesForLLM = lastAssistantMessage |
| #119 | ? [ |
| #120 | formatMessagesForPrompt([lastAssistantMessage])[0], |
| #121 | formatMessagesForPrompt([userMessage])[0] |
| #122 | ] |
| #123 | : [formatMessagesForPrompt([userMessage])[0]]; |
| #124 | |
| #125 | // Check if any message has image content |
| #126 | const hasImage = messagesForLLM.some(msg => { |
| #127 | if (typeof msg.content === 'object' && msg.content !== null) { |
| #128 | const content = msg.content as MessageContent; |
| #129 | return typeof content === 'object' && content !== null && 'type' in content && content.type === 'image_url'; |
| #130 | } |
| #131 | return false; |
| #132 | }); |
| #133 | |
| #134 | // For image messages, only use the text content |
| #135 | if (hasImage) { |
| #136 | messagesForLLM = [ |
| #137 | ...messagesForLLM, |
| #138 | { |
| #139 | role: 'user', |
| #140 | content: userMessage.content |
| #141 | } |
| #142 | ]; |
| #143 | } |
| #144 | |
| #145 | // Fetch relevant memories if there's an image |
| #146 | let relevantMemories = ''; |
| #147 | try { |
| #148 | const searchResponse = await memoryClient.getAll({ |
| #149 | user_id: user |
| #150 | }); |
| #151 | |
| #152 | relevantMemories = searchResponse |
| #153 | .map((memory: Mem0Memory) => `Previous context: ${memory.memory}`) |
| #154 | .join('\n'); |
| #155 | } catch (error) { |
| #156 | console.error('Error fetching memories:', error); |
| #157 | } |
| #158 | |
| #159 | // Add a system message with memories context if there are memories and image |
| #160 | if (relevantMemories.length > 0 && hasImage) { |
| #161 | messagesForLLM = [ |
| #162 | { |
| #163 | role: 'system', |
| #164 | content: `Here are some relevant details about the user:\n${relevantMemories}\n\nPlease use this context when responding to the user's message.` |
| #165 | }, |
| #166 | ...messagesForLLM |
| #167 | ]; |
| #168 | } |
| #169 | |
| #170 | const generateRandomId = () => { |
| #171 | return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); |
| #172 | } |
| #173 | |
| #174 | const completion = await openai.chat.completions.create({ |
| #175 | model: "gpt-4.1-nano-2025-04-14", |
| #176 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment |
| #177 | // @ts-expect-error |
| #178 | messages: messagesForLLM.map(msg => ({ |
| #179 | role: msg.role === 'user' ? 'user' : 'assistant', |
| #180 | content: typeof msg.content === 'object' && msg.content !== null ? [msg.content] : msg.content, |
| #181 | name: generateRandomId(), |
| #182 | })), |
| #183 | stream: true, |
| #184 | }); |
| #185 | |
| #186 | const assistantMessageId = Date.now() + 1; |
| #187 | const assistantMessage: Message = { |
| #188 | id: assistantMessageId.toString(), |
| #189 | content: '', |
| #190 | sender: 'assistant', |
| #191 | timestamp: new Date().toLocaleTimeString(), |
| #192 | }; |
| #193 | |
| #194 | setMessages((prev) => [...prev, assistantMessage]); |
| #195 | |
| #196 | for await (const chunk of completion) { |
| #197 | const textPart = chunk.choices[0]?.delta?.content || ''; |
| #198 | assistantMessage.content += textPart; |
| #199 | setThinking(false); |
| #200 | |
| #201 | setMessages((prev) => |
| #202 | prev.map((msg) => |
| #203 | msg.id === assistantMessageId.toString() |
| #204 | ? { ...msg, content: assistantMessage.content } |
| #205 | : msg |
| #206 | ) |
| #207 | ); |
| #208 | } |
| #209 | } catch (error) { |
| #210 | console.error('Error in sendMessage:', error); |
| #211 | setMessages((prev) => [...prev, ERROR_MESSAGE]); |
| #212 | } finally { |
| #213 | setThinking(false); |
| #214 | } |
| #215 | }; |
| #216 | |
| #217 | return { |
| #218 | messages, |
| #219 | memories: memories || [], |
| #220 | thinking, |
| #221 | sendMessage, |
| #222 | }; |
| #223 | }; |