diff --git a/playground/README.md b/playground/README.md index e466b4e3..e9ef8bde 100644 --- a/playground/README.md +++ b/playground/README.md @@ -17,6 +17,12 @@ Local playground for Ten Agent. - Node.js >= 20 - [pnpm 9.12.3](https://pnpm.io/installation) +### Run local server + +First, make sure you have started the Ten Agent server by following the instructions in the [root README](../README.md#3-start-agent-development-containers). + +Then, you can start the playground web UI by running the following steps. + ### Install dependencies ```bash diff --git a/playground/src/app/layout.tsx b/playground/src/app/layout.tsx index f5370ef2..30f66ec4 100644 --- a/playground/src/app/layout.tsx +++ b/playground/src/app/layout.tsx @@ -1,9 +1,9 @@ // import { ConfigProvider } from "antd"; -import { StoreProvider } from "@/store"; -import type { Metadata, Viewport } from "next"; -import { Toaster } from "@/components/ui/sonner"; +import { StoreProvider } from "@/store" +import type { Metadata, Viewport } from "next" +import { Toaster } from "@/components/ui/sonner" -import "./global.css"; +import "./global.css" export const metadata: Metadata = { title: "TEN Agent | Real-Time Multimodal AI Agent", @@ -13,7 +13,7 @@ export const metadata: Metadata = { capable: true, statusBarStyle: "black", }, -}; +} export const viewport: Viewport = { width: "device-width", @@ -22,12 +22,12 @@ export const viewport: Viewport = { maximumScale: 1, userScalable: false, viewportFit: "cover", -}; +} export default function RootLayout({ children, }: Readonly<{ - children: React.ReactNode; + children: React.ReactNode }>) { return ( @@ -43,8 +43,8 @@ export default function RootLayout({ > */} {children} {/* */} - + - ); + ) } diff --git a/playground/src/components/Chat/ChatCfgSelect.tsx b/playground/src/components/Chat/ChatCfgSelect.tsx index 6da1ca45..499ec1b3 100644 --- a/playground/src/components/Chat/ChatCfgSelect.tsx +++ b/playground/src/components/Chat/ChatCfgSelect.tsx @@ -1,7 +1,7 @@ -"use client"; +"use client" -import * as React from "react"; -import { buttonVariants } from "@/components/ui/button"; +import * as React from "react" +import { buttonVariants } from "@/components/ui/button" import { Select, SelectContent, @@ -10,7 +10,7 @@ import { SelectLabel, SelectTrigger, SelectValue, -} from "@/components/ui/select"; +} from "@/components/ui/select" import { Sheet, SheetContent, @@ -20,7 +20,7 @@ import { SheetTrigger, SheetFooter, SheetClose, -} from "@/components/ui/sheet"; +} from "@/components/ui/sheet" import { Form, FormControl, @@ -29,46 +29,47 @@ import { FormItem, FormLabel, FormMessage, -} from "@/components/ui/form"; -import { Label } from "@/components/ui/label"; -import { Button } from "@/components/ui/button"; -import { Checkbox } from "@/components/ui/checkbox"; -import { Input } from "@/components/ui/input"; -import { Switch } from "@/components/ui/switch"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +} from "@/components/ui/form" +import { Label } from "@/components/ui/label" +import { Button } from "@/components/ui/button" +import { Checkbox } from "@/components/ui/checkbox" +import { Input } from "@/components/ui/input" +import { Switch } from "@/components/ui/switch" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { useAppDispatch, LANGUAGE_OPTIONS, useAppSelector, GRAPH_OPTIONS, useGraphExtensions, -} from "@/common"; -import type { Language } from "@/types"; +} from "@/common" +import type { Language } from "@/types" import { setGraphName, setLanguage, setOverridenPropertiesByGraph, -} from "@/store/reducers/global"; -import { cn } from "@/lib/utils"; -import { SettingsIcon } from "lucide-react"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useForm } from "react-hook-form"; -import { z } from "zod"; +} from "@/store/reducers/global" +import { cn } from "@/lib/utils" +import { SettingsIcon, LoaderCircleIcon } from "lucide-react" +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import { z } from "zod" +import { toast } from "sonner" export function RemoteGraphSelect() { - const dispatch = useAppDispatch(); - const graphName = useAppSelector((state) => state.global.graphName); - const graphs = useAppSelector((state) => state.global.graphs); - const agentConnected = useAppSelector((state) => state.global.agentConnected); + const dispatch = useAppDispatch() + const graphName = useAppSelector((state) => state.global.graphName) + const graphs = useAppSelector((state) => state.global.graphs) + const agentConnected = useAppSelector((state) => state.global.agentConnected) const onGraphNameChange = (val: string) => { - dispatch(setGraphName(val)); - }; + dispatch(setGraphName(val)) + } const graphOptions = graphs.map((item) => ({ label: item, value: item, - })); + })) return ( <> @@ -89,33 +90,33 @@ export function RemoteGraphSelect() { - ); + ) } export function RemoteGraphCfgSheet() { - const dispatch = useAppDispatch(); - const graphExtensions = useGraphExtensions(); - const graphName = useAppSelector((state) => state.global.graphName); + const dispatch = useAppDispatch() + const graphExtensions = useGraphExtensions() + const graphName = useAppSelector((state) => state.global.graphName) const extensionMetadata = useAppSelector( - (state) => state.global.extensionMetadata - ); + (state) => state.global.extensionMetadata, + ) const overridenProperties = useAppSelector( - (state) => state.global.overridenProperties - ); + (state) => state.global.overridenProperties, + ) - const [selectedExtension, setSelectedExtension] = React.useState(""); + const [selectedExtension, setSelectedExtension] = React.useState("") return ( - + Properties Override @@ -131,7 +132,7 @@ export function RemoteGraphCfgSheet() { onValueChange={setSelectedExtension} value={selectedExtension} > - + @@ -146,6 +147,7 @@ export function RemoteGraphCfgSheet() { {graphExtensions?.[selectedExtension]?.["property"] && ( { // clone the overridenProperties let nodesMap = JSON.parse( - JSON.stringify(overridenProperties[selectedExtension] || {}) - ); + JSON.stringify(overridenProperties[selectedExtension] || {}), + ) // Update initial data with any existing overridden values if (overridenProperties[selectedExtension]) { - Object.assign(nodesMap, overridenProperties[selectedExtension]); + Object.assign(nodesMap, overridenProperties[selectedExtension]) } - nodesMap[selectedExtension] = data; - console.log("nodesMap", nodesMap); + nodesMap[selectedExtension] = data + toast.success("Properties updated", { + description: `Graph: ${graphName}, Extension: ${selectedExtension}`, + }) dispatch( setOverridenPropertiesByGraph({ - graphName: selectedExtension, + graphName, nodesMap, - }) - ); + }), + ) }} /> )} @@ -182,7 +186,7 @@ export function RemoteGraphCfgSheet() { */} - ); + ) } // Helper to convert values based on type @@ -190,59 +194,59 @@ const convertToType = (value: any, type: string) => { switch (type) { case "int64": case "int32": - return parseInt(value, 10); + return parseInt(value, 10) case "float64": - return parseFloat(value); + return parseFloat(value) case "bool": - return value === true || value === "true"; + return value === true || value === "true" case "string": - return String(value); + return String(value) default: - return value; + return value } -}; +} const GraphCfgForm = ({ initialData, metadata, onUpdate, }: { - initialData: Record; - metadata: Record; - onUpdate: (data: Record) => void; + initialData: Record + metadata: Record + onUpdate: (data: Record) => void }) => { const formSchema = z.record( z.string(), - z.union([z.string(), z.number(), z.boolean(), z.null()]) - ); + z.union([z.string(), z.number(), z.boolean(), z.null()]), + ) const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: initialData, - }); + }) const onSubmit = (data: z.infer) => { const convertedData = Object.entries(data).reduce( (acc, [key, value]) => { - const type = metadata[key]?.type || "string"; - acc[key] = value === "" ? null : convertToType(value, type); - return acc; + const type = metadata[key]?.type || "string" + acc[key] = value === "" ? null : convertToType(value, type) + return acc }, - {} as Record - ); - onUpdate(convertedData); - }; + {} as Record, + ) + onUpdate(convertedData) + } const initialDataWithType = Object.entries(initialData).reduce( (acc, [key, value]) => { - acc[key] = { value, type: metadata[key]?.type || "string" }; - return acc; + acc[key] = { value, type: metadata[key]?.type || "string" } + return acc }, {} as Record< string, { value: string | number | boolean | null; type: string } - > - ); + >, + ) return (
@@ -279,8 +283,17 @@ const GraphCfgForm = ({ )} /> ))} - +
- ); -}; + ) +}