6-structured_outputs/structured-outputs-assistant-final/components/assistant.tsx (118 lines of code) (raw):
import React, { useState, useCallback, useRef, useEffect } from 'react'
import { Textarea } from '@/components/ui/textarea'
import { Card, CardContent, CardHeader, CardFooter } from '@/components/ui/card'
import { handleTool, handleTurn, Message, SYSTEM_PROMPT } from '@/lib/assistant'
import { motion, AnimatePresence } from 'framer-motion'
import { ArrowRight } from 'lucide-react'
import { Spinner } from './ui/spinner'
import ToolResults from './tool-results'
import Chat from './chat'
import { CartItem } from './cart'
interface AssistantProps {
cart: CartItem[]
setCart: (items: CartItem[]) => void
}
const Assistant: React.FC<AssistantProps> = ({
cart,
setCart
}: AssistantProps) => {
const [inputMessage, setInputMessage] = useState<string>('')
const [chatMessages, setChatMessages] = useState<any[]>([]) // New state for tracking chat messages
const [messages, setMessages] = useState<any[]>([])
const [tool, setTool] = useState<Tool | null>(null)
const [loading, setLoading] = useState<boolean>(false)
const processMessages = useCallback(async (currentMessages: Message[]) => {
setLoading(true)
console.log('send message', currentMessages)
try {
const response = await handleTurn(currentMessages)
console.log('response', response)
if (response.tool_calls[0].function) {
const toolName = response.tool_calls[0].function.name
const args = JSON.parse(response.tool_calls[0].function.arguments)
const toolCallResult = await handleTool(toolName, args)
if (toolName === 'add_to_cart') {
setCart([...toolCallResult.cartItems])
}
setTool({
name: toolName,
content: toolCallResult
})
const updatedMessages = [
...currentMessages,
{ ...response },
{
name: toolName,
role: 'tool',
tool_call_id: response.tool_calls[0].id,
content: JSON.stringify(toolCallResult)
}
]
setMessages(updatedMessages)
setChatMessages(prevChatMessages => [
...prevChatMessages,
{
role: 'tool',
name: toolName,
content: toolCallResult
}
])
await processMessages(updatedMessages)
} else {
// Handle assistant response without tool calls
const updatedMessages = [...currentMessages, response]
setMessages(updatedMessages)
setChatMessages(prevChatMessages => [
...prevChatMessages,
{ role: 'assistant', content: response.content }
])
}
} catch (error) {
console.error('Error processing message:', error)
setMessages(prevMessages => [
...prevMessages,
{
role: 'assistant',
content:
'There was an error processing your request. Please try again.'
}
])
} finally {
setLoading(false)
}
}, [])
const handleSendMessage = useCallback(
async (message: string) => {
console.log('inputMessage', message)
if (!message.trim()) return
const newMessages = [...messages]
if (newMessages.length === 0) {
newMessages.push({ role: 'system', content: SYSTEM_PROMPT })
}
newMessages.push({
role: 'user',
content: `USER INPUT: ${message.trim()}\n\nCURRENT CART: ${JSON.stringify(
cart
)}`
})
setMessages(newMessages)
setChatMessages(prevChatMessages => [
...prevChatMessages,
{ role: 'user', content: message.trim() }
])
setInputMessage('')
await processMessages(newMessages)
},
[inputMessage, messages, processMessages]
)
return (
<div className="p-4 w-full h-screen">
<Chat
messages={chatMessages}
onSendMessage={handleSendMessage}
tool={tool}
loading={loading}
/>
</div>
)
}
export default Assistant