hts/apps/staffai/components/chat.tsx

344 lines
11 KiB
TypeScript

'use client'
import { usePathname, useRouter } from 'next/navigation'
import { PrefetchKind } from 'next/dist/client/components/router-reducer/router-reducer-types'
import toast from 'react-hot-toast'
import { cn } from '@/lib/utils'
import { ChatList } from '@/components/chat-list'
import { ChatPanel } from '@/components/chat-panel'
import { ChatScrollAnchor } from '@/components/chat-scroll-anchor'
import { nanoid } from '@/lib/utils'
import { functionSchemas } from '@/lib/functions/schemas'
import { Landing } from '@/components/landing'
import { useGlobalStore } from '@/app/state/global-store'
import { useW3GPTDeploy } from '@/lib/hooks/use-w3gpt-deploy'
// import { useNetwork } from 'wagmi'
import { useEffect, useState } from 'react'
import { FadeIn } from './landing/fade-in'
import Image from 'next/image';
import logoImage from '@/components/images/logo.png';
import { useTranslation } from 'react-i18next'
import { Button, Modal } from 'antd'
import { type Message, useChat } from '@/lib/hooks/use-chat'
import { ChatRequest, FunctionCallHandler } from '@aigxion/isdk'
export interface ChatProps extends React.ComponentProps<'div'> {
initialMessages?: Message[]
id?: string
showLanding?: boolean
avatarUrl?: string | null | undefined
}
export function Chat({
id,
initialMessages,
className,
showLanding = false,
avatarUrl
}: ChatProps) {
const router = useRouter()
const path = usePathname()
const { t, i18n } = useTranslation()
const isChatPage = path.includes('chat')
const {
setIsGenerating,
setIsDeploying,
setDeployContractConfig,
verifyContractConfig,
lastDeploymentData,
setLastDeploymentData
} = useGlobalStore()
// const { chain } = useNetwork()
// const fallbackChainId = chain?.unsupported === false ? chain.id : 5001
// const activeChainId = chain?.id ?? fallbackChainId
// const { deploy } = useW3GPTDeploy({ chainId: activeChainId })
useEffect(() => {
let isLounted = true
async function verifyContract() {
}
verifyContract()
// try {
// let data = localStorage.getItem("UserData");
// if (data == null) return
// data = JSON.parse(data)
// console.log("UserData", data)
// } catch (error) {
// console.log(error)
// }
return () => {
isLounted = false
}
}, [])
useEffect(() => {
let isMounted = true
async function verifyContract() {
if (
!verifyContractConfig?.deployHash ||
lastDeploymentData?.verificationStatus === 'success'
) {
return
}
try {
const response = await fetch('/api/verify-contract', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(verifyContractConfig)
})
const data = await response.json()
if (typeof data === 'string' && data.startsWith('0x') && isMounted) {
toast.success('Contract verified successfully!')
lastDeploymentData &&
setLastDeploymentData({
...lastDeploymentData,
verificationStatus: 'success'
})
} else {
setTimeout(verifyContract, 15000) // Retry after 15 seconds
}
} catch (error) {
console.error('Verification failed', error)
setTimeout(verifyContract, 15000) // Retry after 15 seconds
}
}
verifyContract()
return () => {
isMounted = false
}
}, [lastDeploymentData, verifyContractConfig, setLastDeploymentData])
const functionCallHandler: FunctionCallHandler = async (
chatMessages,
functionCall
) => {
if (functionCall.name === 'deploy_contract') {
setIsDeploying(true)
const { chainId, contractName, sourceCode, constructorArgs } = JSON.parse(
functionCall.arguments || '{}'
)
// setDeployContractConfig({
// chainId: chainId ?? activeChainId,
// contractName,
// sourceCode,
// constructorArgs
// })
// const verifiedContractAddress = await deploy({
// chainId: chainId ?? activeChainId,
// contractName,
// sourceCode,
// constructorArgs
// })
// const functionResponse: ChatRequest = {
// messages: [
// ...chatMessages,
// {
// id: nanoid(),
// name: 'deploy_contract',
// role: 'function',
// content: JSON.stringify({ verifiedContractAddress })
// }
// ],
// functions: functionSchemas
// }
// return functionResponse
} else if (functionCall.name === 'text_to_image') {
const response = await fetch('/api/text-to-image', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ text: functionCall.arguments })
})
if (!response.ok) {
throw new Error(response.statusText)
}
const { imageUrl, metadataUrl } = await response.json()
const functionResponse: ChatRequest = {
messages: [
...chatMessages,
{
id: nanoid(),
name: 'text-to-image',
role: 'function',
content: JSON.stringify({ imageUrl, metadataUrl })
}
],
functions: functionSchemas
}
return functionResponse
}
}
const { messages, append, reload, stop, isLoading, input, setInput } =
useChat({
experimental_onFunctionCall: functionCallHandler,
initialMessages,
id,
body: {
id
},
onResponse(response) {
setIsGenerating(true)
if (!isChatPage) {
// router.prefetch(`${i18n.language}/chat/${id}`, {
// kind: PrefetchKind.FULL
// })
}
if (response.status === 401) {
toast.error(response.statusText)
}
},
onFinish() {
setIsGenerating(false)
if (!isChatPage) {
// history.pushState({}, '', `${i18n.language}/chat/${id}`)
// history.go(1)
}
}
})
return (
<>
<div className={cn('px-4 pt-4 md:pt-10 h-full relative', className)}>
<FadeIn >
{/* {showLanding && <Landing disableAnimations={isChatPage} />} */}
{/* {showLanding && <h3 className='text-center text-2xl font-bold py-4'>What do you want to ask about AI or Blockchain?</h3>} */}
{
showLanding &&
// <Image src={logoImage} height={40} alt="show" className="cursor-pointer m-auto" onClick={() => { }} />
<LogoAI
className='flex text-center m-auto'
/>
}
</FadeIn>
<ChatList messages={messages} avatarUrl={avatarUrl} />
<ChatScrollAnchor trackVisibility={isLoading} />
{!!messages && <ChatPanel
id={id}
stop={stop}
isLoading={isLoading}
append={append}
reload={reload}
messages={messages}
input={input}
setInput={setInput}
/>}
</div>
</>
)
}
export const LogoAI: React.FC<{ className?: string, width?: string }> = ({ className, width }) => {
return (
<svg
className={className}
width={!!width ? width : "186"}
height="40"
version="1.1"
id=""
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="0 0 3477.56 1000"
style={{
// 废弃属性
// enableBackground: 'new 0 0 3477.56 1000'
}}
xmlSpace="preserve"
>
<style>
{`
.st0{opacity:0.24;}
.st1{fill:#2565D5;}
.st2{fill:#36C072;}
.st3{fill:#191919;}
`}
</style>
<g>
<g>
</g>
</g>
<g className="st0">
</g>
<g>
<polygon className="st1" points="643.09,378.66 536.6,560.35 701.54,833.7 746.33,907.97 963.7,907.97 " />
<polygon className="st2" points="500.55,143.33 392.41,321.38 500.58,500.65 608.43,321.43 " />
<polygon className="st1" points="36.3,907.97 253.67,907.97 422.84,629.82 465.37,559.14 357.45,379.28 " />
</g>
<g>
<polygon className="st1" points="1616.79,1237.59 1615.18,1240.33 1617.67,1244.46 1618.34,1245.58 1621.62,1245.58 " />
<polygon className="st2" points="1614.64,1234.04 1613.01,1236.73 1614.64,1239.43 1616.27,1236.73 " />
<polygon className="st1" points="1607.64,1245.58 1610.92,1245.58 1613.47,1241.38 1614.11,1240.32 1612.48,1237.6 " />
</g>
<g>
<g>
<g>
<path className="st3" d="M1130.46,895.66v-86.92c15.93,2.41,31.63,3.62,47.07,3.62c27.04,0,46-7.6,56.86-22.82
c10.86-15.21,16.3-37.77,16.3-67.72V260.44h109.37V726.9c0,56.97-14.97,99.95-44.9,128.93c-29.95,28.97-72.44,43.46-127.48,43.46
C1167.89,899.28,1148.82,898.07,1130.46,895.66z"/>
<path className="st3" d="M1802.61,590.73l-252.06,15.93c1.93,28.01,11.34,48.53,28.25,61.57c16.9,13.04,42.73,19.56,77.51,19.56
c23.17,0,47.19-2.77,72.06-8.33c24.87-5.54,44.06-11.95,57.59-19.2v82.58c-12.57,7.24-32.59,13.65-60.12,19.2
c-27.53,5.54-57.47,8.33-89.82,8.33c-42.01,0-76.9-6.04-104.66-18.11c-27.77-12.07-49.02-32.96-63.74-62.65
c-14.74-29.7-22.09-70.37-22.09-122.04c0-51.66,7.48-92.35,22.46-122.04c14.96-29.7,36.45-50.58,64.46-62.65
c28-12.07,63.49-18.11,106.48-18.11c43.46,0,77.5,7,102.12,21c24.63,14.01,41.65,33.45,51.07,58.31
c9.42,24.88,14.12,55.66,14.12,92.35C1806.24,554.27,1805.03,572.38,1802.61,590.73z M1710.63,521.19
c0-24.63-4.83-43.69-14.49-57.22c-9.67-13.51-28.73-20.28-57.22-20.28c-31.87,0-54.21,6.88-67,20.64
c-12.8,13.76-19.92,36.83-21.37,69.17L1710.63,521.19z"/>
<path className="st3" d="M1914.52,743.55c-16.66-15.93-24.99-41.76-24.99-77.5V212.63h105.02V640.7c0,16.91,2.41,28.38,7.25,34.41
c4.82,6.04,13.28,9.06,25.35,9.06c8.21,0,15.21-0.73,21.01-2.18v81.12c-13.53,2.9-28.5,4.35-44.91,4.35
C1960.75,767.46,1931.18,759.49,1914.52,743.55z"/>
<path className="st3" d="M2134.7,743.55c-16.66-15.93-24.99-41.76-24.99-77.5V212.63h105.02V640.7c0,16.91,2.41,28.38,7.25,34.41
c4.82,6.04,13.28,9.06,25.35,9.06c8.21,0,15.21-0.73,21.01-2.18v81.12c-13.53,2.9-28.5,4.35-44.91,4.35
C2180.93,767.46,2151.36,759.49,2134.7,743.55z"/>
<path className="st3" d="M2326.28,817.44c31.87,0,55.17-4.6,69.9-13.76c14.72-9.18,25.23-23.9,31.51-44.19l-152.1-387.51h116.61
l91.99,278.86h4.35l86.92-278.86h112.99l-131.1,354.92c-17.87,48.76-35.85,85.83-53.96,111.18
c-18.1,25.35-40.69,43.33-67.72,53.96c-27.05,10.61-63.51,15.93-109.38,15.93V817.44z"/>
<path className="st3" d="M3056.38,638.53h-204.26l-43.46,124.58h-114.45l187.6-502.67h147.76l186.15,502.67h-116.62L3056.38,638.53z
M3024.5,547.27l-65.18-190.5h-8.69l-66.64,190.5H3024.5z"/>
<path className="st3" d="M3283.44,314.03c-7.97-2.41-13.53-7.48-16.66-15.21c-3.15-7.72-4.7-20.03-4.7-36.94
c0-16.41,1.56-28.36,4.7-35.85c3.14-7.48,8.69-12.42,16.66-14.85c7.96-2.41,20.39-3.62,37.3-3.62c17.39,0,29.93,1.21,37.66,3.62
c7.72,2.43,13.15,7.25,16.3,14.49c3.14,7.25,4.7,19.32,4.7,36.22c0,16.91-1.57,29.22-4.7,36.94c-3.15,7.73-8.69,12.8-16.66,15.21
c-7.96,2.43-20.4,3.62-37.3,3.62C3303.84,317.66,3291.41,316.46,3283.44,314.03z M3267.87,371.98h105.02v391.13h-105.02V371.98z"
/>
</g>
</g>
</g>
</svg>
)
};