ten / demo /src /components /Dialog /Settings.tsx
3v324v23's picture
Зафиксирована рабочая версия TEN-Agent для HuggingFace Space
87337b1
raw
history blame contribute delete
13.9 kB
"use client"
import * as React from "react"
import { Button } from "@/components/ui/button"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import { Textarea } from "@/components/ui/textarea"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { SettingsIcon, EraserIcon, ShieldCheckIcon } from "lucide-react"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { z } from "zod"
import { toast } from "sonner"
import { useAppDispatch, useAppSelector } from "@/common/hooks"
import { ECozeBaseUrl } from "@/common/constant"
import {
setAgentSettings,
setCozeSettings,
resetCozeSettings,
resetDifySettings,
setGlobalSettingsDialog,
setDifySettings,
} from "@/store/reducers/global"
const TABS_OPTIONS = [
{
label: "Agent",
value: "agent",
},
{
label: "Coze",
value: "coze",
},
{
label: "Dify",
value: "dify",
},
]
export const useSettingsTabs = () => {
const [tabs, setTabs] = React.useState(TABS_OPTIONS)
const graphName = useAppSelector((state) => state.global.graphName)
const enableCozeSettingsMemo = React.useMemo(() => {
return isCozeGraph(graphName)
}, [graphName])
const enableDifySettingsMemo = React.useMemo(() => {
return isDifyGraph(graphName)
}, [graphName])
const enableGreetingsOrPromptMemo: { greeting: boolean, prompt: boolean } = React.useMemo(() => {
if (graphName === "va_gemini_v2v") {
return {
greeting: false,
prompt: true,
}
} else if (graphName === "va_dify_azure") {
return {
greeting: true,
prompt: false,
}
} else if (graphName === "story_teller_stt_integrated") {
return {
greeting: true,
prompt: false,
}
}
return {
greeting: true,
prompt: true,
}
}, [graphName])
React.useEffect(() => {
if (enableCozeSettingsMemo) {
setTabs((prev) =>
[
{ label: "Agent", value: "agent" },
{ label: "Coze", value: "coze" },
]
)
} else if (enableDifySettingsMemo) {
setTabs((prev) =>
[
{ label: "Agent", value: "agent" },
{ label: "Dify", value: "dify" },
]
)
} else {
setTabs((prev) => prev.filter((tab) => tab.value !== "coze" && tab.value !== "dify"))
}
}, [enableCozeSettingsMemo, enableDifySettingsMemo])
return {
tabs,
enableGreetingsOrPromptMemo,
}
}
export default function SettingsDialog() {
const dispatch = useAppDispatch()
const globalSettingsDialog = useAppSelector(
(state) => state.global.globalSettingsDialog,
)
const { tabs, enableGreetingsOrPromptMemo } = useSettingsTabs()
const handleClose = () => {
dispatch(setGlobalSettingsDialog({ open: false, tab: undefined }))
}
return (
<Dialog
open={globalSettingsDialog.open}
onOpenChange={(open) =>
dispatch(setGlobalSettingsDialog({ open, tab: undefined }))
}
>
<DialogTrigger asChild>
<Button variant="ghost" size="sm" className="w-9">
<SettingsIcon />
</Button>
</DialogTrigger>
<DialogContent className="w-fit">
<DialogHeader>
<DialogTitle>Settings</DialogTitle>
</DialogHeader>
<Tabs
defaultValue={globalSettingsDialog.tab || TABS_OPTIONS[0].value}
className="w-full min-w-96 max-w-screen-sm"
>
{tabs.length > 1 && (
<TabsList className="w-full">
{tabs.map((tab) => (
<TabsTrigger
key={tab.value}
className="w-full"
value={tab.value}
>
{tab.label}
</TabsTrigger>
))}
</TabsList>
)}
<TabsContent value="agent">
<CommonAgentSettingsTab
enableGreeting={enableGreetingsOrPromptMemo.greeting}
enablePrompt={enableGreetingsOrPromptMemo.prompt}
handleClose={handleClose}
handleSubmit={handleClose}
/>
</TabsContent>
<TabsContent value="coze">
<CozeSettingsTab
handleClose={handleClose}
handleSubmit={handleClose}
/>
</TabsContent>
<TabsContent value="dify">
<DifySettingsTab
handleClose={handleClose}
handleSubmit={handleClose}
/>
</TabsContent>
</Tabs>
</DialogContent>
</Dialog>
)
}
const formSchema = z.object({
greeting: z.string().optional(),
prompt: z.string().optional(),
})
export function CommonAgentSettingsTab(props: {
enableGreeting?: boolean
enablePrompt?: boolean
handleClose?: () => void
handleSubmit?: (values: z.infer<typeof formSchema>) => void
}) {
const { handleSubmit, enableGreeting, enablePrompt } = props
const dispatch = useAppDispatch()
const agentSettings = useAppSelector((state) => state.global.agentSettings)
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
greeting: agentSettings.greeting,
prompt: agentSettings.prompt,
},
})
function onSubmit(values: z.infer<typeof formSchema>) {
console.log("Form Values:", values)
dispatch(setAgentSettings(values))
handleSubmit?.(values)
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
{enableGreeting && <FormField
control={form.control}
name="greeting"
render={({ field }) => (
<FormItem>
<FormLabel>Greeting</FormLabel>
<FormControl>
<Textarea
placeholder="Enter the greeting, leave it blank if you want to use default one."
className="resize-none"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>}
{enablePrompt && <FormField
control={form.control}
name="prompt"
render={({ field }) => (
<FormItem>
<FormLabel>Prompt</FormLabel>
<FormControl>
<Textarea
placeholder="Enter the prompt, leave it blank if you want to use default one."
className="resize-none"
rows={4}
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>}
<DialogFooter>
<Button type="submit" disabled={!form.formState.isValid}>
Save Agent Settings
</Button>
</DialogFooter>
</form>
</Form>
)
}
export const cozeSettingsFormSchema = z.object({
token: z
.string({
message: "Token is required",
})
.min(1),
bot_id: z
.string({
message: "Bot ID is required",
})
.min(1),
base_url: z.nativeEnum(ECozeBaseUrl).default(ECozeBaseUrl.GLOBAL),
})
export const isCozeGraph = (graphName: string) => {
return graphName.toLowerCase().includes("coze")
}
export function CozeSettingsTab(props: {
handleClose?: () => void
handleSubmit?: (values: z.infer<typeof cozeSettingsFormSchema>) => void
}) {
const { handleSubmit } = props
const dispatch = useAppDispatch()
const cozeSettings = useAppSelector((state) => state.global.cozeSettings)
const form = useForm<z.infer<typeof cozeSettingsFormSchema>>({
resolver: zodResolver(cozeSettingsFormSchema),
defaultValues: {
token: cozeSettings.token,
bot_id: cozeSettings.bot_id,
base_url: cozeSettings.base_url as z.infer<
typeof cozeSettingsFormSchema
>["base_url"],
},
})
function onSubmit(values: z.infer<typeof cozeSettingsFormSchema>) {
console.log("Coze Form Values:", values)
dispatch(setCozeSettings(values))
handleSubmit?.(values)
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="bot_id"
render={({ field }) => (
<FormItem>
<FormLabel>Bot ID*</FormLabel>
<FormControl>
<Input placeholder="Enter your Coze bot ID" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="token"
render={({ field }) => (
<FormItem>
<FormLabel>Token*</FormLabel>
<FormControl>
<Input placeholder="Enter your Coze API token" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="base_url"
render={({ field }) => (
<FormItem>
<FormLabel>Base URL*</FormLabel>
<FormControl>
<Select {...field} onValueChange={field.onChange}>
<SelectTrigger>
<SelectValue placeholder="Select base URL" />
</SelectTrigger>
<SelectContent>
<SelectItem value={ECozeBaseUrl.GLOBAL}>
{ECozeBaseUrl.GLOBAL}
</SelectItem>
<SelectItem value={ECozeBaseUrl.CN}>
{ECozeBaseUrl.CN}
</SelectItem>
{/* <SelectItem value={ECozeBaseUrl.CN}>
{ECozeBaseUrl.CN}
</SelectItem> */}
</SelectContent>
</Select>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="flex flex-col items-end">
<div className="flex items-center gap-2">
<Button
variant="destructive"
size="icon"
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
form.reset({
token: "",
bot_id: "",
base_url: ECozeBaseUrl.GLOBAL,
})
dispatch(resetCozeSettings())
toast.success("Coze settings reset")
}}
>
<EraserIcon />
</Button>
<Button type="submit" disabled={!form.formState.isValid}>
Save Coze Settings
</Button>
</div>
<Label className="flex select-none items-center gap-1 pt-2 text-right text-xs text-muted-foreground">
<ShieldCheckIcon className="me-1 size-3" />
Settings are saved in your browser only
</Label>
</div>
</form>
</Form>
)
}
export const difySettingsFormSchema = z.object({
api_key: z
.string({
message: "API Key is required",
})
.min(1),
})
export const isDifyGraph = (graphName: string) => {
return graphName.toLowerCase().includes("dify")
}
export function DifySettingsTab(props: {
handleClose?: () => void
handleSubmit?: (values: z.infer<typeof difySettingsFormSchema>) => void
}) {
const { handleSubmit } = props
const dispatch = useAppDispatch()
const difySettings = useAppSelector((state) => state.global.difySettings)
const form = useForm<z.infer<typeof difySettingsFormSchema>>({
resolver: zodResolver(difySettingsFormSchema),
defaultValues: {
api_key: difySettings.api_key,
},
})
function onSubmit(values: z.infer<typeof difySettingsFormSchema>) {
console.log("Dify Form Values:", values)
dispatch(setDifySettings(values))
handleSubmit?.(values)
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="api_key"
render={({ field }) => (
<FormItem>
<FormLabel>API Key*</FormLabel>
<FormControl>
<Input placeholder="Enter your Dify API Key" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="flex flex-col items-end">
<div className="flex items-center gap-2">
<Button
variant="destructive"
size="icon"
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
form.reset({
api_key: "",
})
dispatch(resetDifySettings())
toast.success("Dify settings reset")
}}
>
<EraserIcon />
</Button>
<Button type="submit" disabled={!form.formState.isValid}>
Save Dify Settings
</Button>
</div>
<Label className="flex select-none items-center gap-1 pt-2 text-right text-xs text-muted-foreground">
<ShieldCheckIcon className="me-1 size-3" />
Settings are saved in your browser only
</Label>
</div>
</form>
</Form>
)
}