From 20009aed53d8864c9204d43a17895168a777d2cc Mon Sep 17 00:00:00 2001 From: Ilan Bigio Date: Mon, 16 Dec 2024 13:06:08 -0800 Subject: Initial commit --- webapp/components/session-configuration-panel.tsx | 291 ++++++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 webapp/components/session-configuration-panel.tsx (limited to 'webapp/components/session-configuration-panel.tsx') diff --git a/webapp/components/session-configuration-panel.tsx b/webapp/components/session-configuration-panel.tsx new file mode 100644 index 0000000..85c9fc1 --- /dev/null +++ b/webapp/components/session-configuration-panel.tsx @@ -0,0 +1,291 @@ +import React, { useState, useEffect } from "react"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Textarea } from "@/components/ui/textarea"; +import { Button } from "@/components/ui/button"; +import { Plus, Edit, Trash, Check, AlertCircle } from "lucide-react"; +import { toolTemplates } from "@/lib/tool-templates"; +import { ToolConfigurationDialog } from "./tool-configuration-dialog"; +import { BackendTag } from "./backend-tag"; +import { useBackendTools } from "@/lib/use-backend-tools"; + +interface SessionConfigurationPanelProps { + callStatus: string; + onSave: (config: any) => void; +} + +const SessionConfigurationPanel: React.FC = ({ + callStatus, + onSave, +}) => { + const [instructions, setInstructions] = useState( + "You are a helpful assistant in a phone call." + ); + const [voice, setVoice] = useState("ash"); + const [tools, setTools] = useState([]); + const [editingIndex, setEditingIndex] = useState(null); + const [editingSchemaStr, setEditingSchemaStr] = useState(""); + const [isJsonValid, setIsJsonValid] = useState(true); + const [openDialog, setOpenDialog] = useState(false); + const [selectedTemplate, setSelectedTemplate] = useState(""); + const [saveStatus, setSaveStatus] = useState< + "idle" | "saving" | "saved" | "error" + >("idle"); + const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); + + // Custom hook to fetch backend tools every 3 seconds + const backendTools = useBackendTools("http://localhost:8081/tools", 3000); + + // Track changes to determine if there are unsaved modifications + useEffect(() => { + setHasUnsavedChanges(true); + }, [instructions, voice, tools]); + + // Reset save status after a delay when saved + useEffect(() => { + if (saveStatus === "saved") { + const timer = setTimeout(() => { + setSaveStatus("idle"); + }, 3000); + return () => clearTimeout(timer); + } + }, [saveStatus]); + + const handleSave = async () => { + setSaveStatus("saving"); + try { + await onSave({ + instructions, + voice, + tools: tools.map((tool) => JSON.parse(tool)), + }); + setSaveStatus("saved"); + setHasUnsavedChanges(false); + } catch (error) { + setSaveStatus("error"); + } + }; + + const handleAddTool = () => { + setEditingIndex(null); + setEditingSchemaStr(""); + setSelectedTemplate(""); + setIsJsonValid(true); + setOpenDialog(true); + }; + + const handleEditTool = (index: number) => { + setEditingIndex(index); + setEditingSchemaStr(tools[index] || ""); + setSelectedTemplate(""); + setIsJsonValid(true); + setOpenDialog(true); + }; + + const handleDeleteTool = (index: number) => { + const newTools = [...tools]; + newTools.splice(index, 1); + setTools(newTools); + }; + + const handleDialogSave = () => { + try { + JSON.parse(editingSchemaStr); + } catch { + return; + } + const newTools = [...tools]; + if (editingIndex === null) { + newTools.push(editingSchemaStr); + } else { + newTools[editingIndex] = editingSchemaStr; + } + setTools(newTools); + setOpenDialog(false); + }; + + const handleTemplateChange = (val: string) => { + setSelectedTemplate(val); + + // Determine if the selected template is from local or backend + let templateObj = + toolTemplates.find((t) => t.name === val) || + backendTools.find((t: any) => t.name === val); + + if (templateObj) { + setEditingSchemaStr(JSON.stringify(templateObj, null, 2)); + setIsJsonValid(true); + } + }; + + const onSchemaChange = (value: string) => { + setEditingSchemaStr(value); + try { + JSON.parse(value); + setIsJsonValid(true); + } catch { + setIsJsonValid(false); + } + }; + + const getToolNameFromSchema = (schema: string): string => { + try { + const parsed = JSON.parse(schema); + return parsed?.name || "Untitled Tool"; + } catch { + return "Invalid JSON"; + } + }; + + const isBackendTool = (name: string): boolean => { + return backendTools.some((t: any) => t.name === name); + }; + + return ( + + +
+ + Session Configuration + +
+ {saveStatus === "error" ? ( + + + Save failed + + ) : hasUnsavedChanges ? ( + Not saved + ) : ( + + + Saved + + )} +
+
+
+ + +
+
+ +