3v324v23's picture
Зафиксирована рабочая версия TEN-Agent для HuggingFace Space
87337b1
"use client"
import * as React from "react"
import { LoadingButton } from "@/components/Button/LoadingButton"
import {
setAgentConnected,
setMobileActiveTab,
setGlobalSettingsDialog,
} from "@/store/reducers/global"
import {
useAppDispatch,
useAppSelector,
apiPing,
apiStartService,
apiStopService,
MOBILE_ACTIVE_TAB_MAP,
EMobileActiveTab,
type StartRequestConfig,
} from "@/common"
import { toast } from "sonner"
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { cn } from "@/lib/utils"
import SettingsDialog, {
isCozeGraph,
cozeSettingsFormSchema,
isDifyGraph,
difySettingsFormSchema,
} from "@/components/Dialog/Settings"
let intervalId: NodeJS.Timeout | null = null
export default function Action(props: { className?: string }) {
const { className } = props
const dispatch = useAppDispatch()
const agentConnected = useAppSelector((state) => state.global.agentConnected)
const channel = useAppSelector((state) => state.global.options.channel)
const userId = useAppSelector((state) => state.global.options.userId)
const language = useAppSelector((state) => state.global.language)
const voiceType = useAppSelector((state) => state.global.voiceType)
const graphName = useAppSelector((state) => state.global.graphName)
const agentSettings = useAppSelector((state) => state.global.agentSettings)
const cozeSettings = useAppSelector((state) => state.global.cozeSettings)
const difySettings = useAppSelector((state) => state.global.difySettings)
const mobileActiveTab = useAppSelector(
(state) => state.global.mobileActiveTab,
)
const [loading, setLoading] = React.useState(false)
React.useEffect(() => {
if (channel) {
checkAgentConnected()
}
}, [channel])
const checkAgentConnected = async () => {
const res: any = await apiPing(channel)
if (res?.code == 0) {
dispatch(setAgentConnected(true))
}
}
const onClickConnect = async () => {
if (loading) {
return
}
setLoading(true)
try {
if (agentConnected) {
// handle disconnect
await apiStopService(channel)
dispatch(setAgentConnected(false))
toast.success("Agent disconnected")
stopPing()
} else {
// handle connect
// prepare start service payload
const startServicePayload: StartRequestConfig = {
channel,
userId,
graphName,
language,
voiceType,
greeting: agentSettings.greeting,
prompt: agentSettings.prompt,
}
// check graph ---
if (isCozeGraph(graphName)) {
// check coze settings
const cozeSettingsResult =
cozeSettingsFormSchema.safeParse(cozeSettings)
if (!cozeSettingsResult.success) {
dispatch(
setGlobalSettingsDialog({
open: true,
tab: "coze",
}),
)
throw new Error(
"Invalid Coze settings. Please check your settings.",
)
}
startServicePayload.coze_token = cozeSettingsResult.data.token
startServicePayload.coze_bot_id = cozeSettingsResult.data.bot_id
startServicePayload.coze_base_url = cozeSettingsResult.data.base_url
} else if (isDifyGraph(graphName)) {
const difySettingsResult = difySettingsFormSchema.safeParse(difySettings)
if (!difySettingsResult.success) {
dispatch(
setGlobalSettingsDialog({
open: true,
tab: "dify",
}),
)
throw new Error(
"Invalid Dify settings. Please check your settings.",
)
}
startServicePayload.dify_api_key = difySettingsResult.data.api_key
}
// common -- start service
const res = await apiStartService(startServicePayload)
const { code, msg } = res || {}
if (code != 0) {
if (code == "10001") {
toast.error(
"The number of users experiencing the program simultaneously has exceeded the limit. Please try again later.",
)
} else {
toast.error(`code:${code},msg:${msg}`)
}
throw new Error(msg)
}
dispatch(setAgentConnected(true))
toast.success("Agent connected")
startPing()
}
} catch (error) {
console.error(error)
toast.error("Failed to connect/disconnect agent", {
description: (error as Error)?.message,
})
} finally {
setLoading(false)
}
}
const startPing = () => {
if (intervalId) {
stopPing()
}
intervalId = setInterval(() => {
apiPing(channel)
}, 3000)
}
const stopPing = () => {
if (intervalId) {
clearInterval(intervalId)
intervalId = null
}
}
const onChangeMobileActiveTab = (tab: string) => {
dispatch(setMobileActiveTab(tab as EMobileActiveTab))
}
return (
<>
{/* Action Bar */}
<div
className={cn(
"mx-2 mt-2 flex items-center justify-between rounded-t-lg bg-[#181a1d] p-2 md:m-2 md:rounded-lg",
className,
)}
>
{/* -- Description Part */}
<div className="hidden md:block">
<span className="text-sm font-bold"></span>
<span className="ml-2 text-xs text-muted-foreground">
</span>
</div>
<Tabs
defaultValue={mobileActiveTab}
className="w-[400px] md:hidden"
onValueChange={onChangeMobileActiveTab}
>
<TabsList>
{Object.values(EMobileActiveTab).map((tab) => (
<TabsTrigger key={tab} value={tab} className="w-24 text-sm">
{MOBILE_ACTIVE_TAB_MAP[tab]}
</TabsTrigger>
))}
</TabsList>
</Tabs>
{/* -- Action Button */}
<div className="ml-auto flex items-center gap-2">
<SettingsDialog />
<LoadingButton
onClick={onClickConnect}
variant={!agentConnected ? "default" : "destructive"}
size="sm"
className="w-fit min-w-24"
loading={loading}
svgProps={{ className: "h-4 w-4 text-muted-foreground" }}
>
{loading
? "Connecting"
: !agentConnected
? "Connect"
: "Disconnect"}
</LoadingButton>
</div>
</div>
</>
)
}