diff --git a/packages/ui/src/components/agent-creator-dialog.tsx b/packages/ui/src/components/agent-creator-dialog.tsx new file mode 100644 index 0000000..106c94e --- /dev/null +++ b/packages/ui/src/components/agent-creator-dialog.tsx @@ -0,0 +1,327 @@ +import { Dialog } from "@kobalte/core/dialog" +import { Bot, Loader2, Sparkles, X } from "lucide-solid" +import { Component, Show, createSignal } from "solid-js" +import { Portal } from "solid-js/web" +import { updateInstanceConfig } from "../stores/instance-config" +import { fetchAgents } from "../stores/sessions" +import { showToastNotification } from "../lib/notifications" +import { getLogger } from "../lib/logger" + +const log = getLogger("agent-creator") + +const MAX_PROMPT_LENGTH = 30000 + +interface AgentCreatorDialogProps { + instanceId: string + open: boolean + onClose: () => void +} + +const AgentCreatorDialog: Component = (props) => { + const [name, setName] = createSignal("") + const [description, setDescription] = createSignal("") + const [prompt, setPrompt] = createSignal("") + const [isGenerating, setIsGenerating] = createSignal(false) + const [isSaving, setIsSaving] = createSignal(false) + const [useAiGeneration, setUseAiGeneration] = createSignal(true) + + const resetForm = () => { + setName("") + setDescription("") + setPrompt("") + setIsGenerating(false) + setUseAiGeneration(true) + } + + const handleClose = () => { + resetForm() + props.onClose() + } + + const generatePromptWithAI = async () => { + if (!name().trim() || !description().trim()) { + showToastNotification({ + title: "Missing Information", + message: "Please provide both name and description to generate an agent prompt.", + variant: "warning", + duration: 5000, + }) + return + } + + setIsGenerating(true) + try { + // Use Z.AI or another endpoint to generate the prompt + const response = await fetch("/api/zai/chat", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + model: "glm-4.5-flash", + messages: [ + { + role: "system", + content: `You are an expert AI agent prompt designer. Generate a comprehensive, detailed system prompt for an AI coding assistant agent based on the user's requirements. The prompt should: +1. Define the agent's role and expertise +2. Specify its capabilities and limitations +3. Include guidelines for code style and best practices +4. Define how it should interact with users +5. Include any domain-specific knowledge relevant to the description + +Output ONLY the agent system prompt, no explanations or markdown formatting.`, + }, + { + role: "user", + content: `Create a system prompt for an AI coding agent with the following details: + +Name: ${name()} +Purpose: ${description()} + +Generate a comprehensive system prompt that will make this agent effective at its purpose.`, + }, + ], + stream: false, + max_tokens: 4096, + }), + }) + + if (!response.ok) { + throw new Error(`Generation failed: ${response.status}`) + } + + const data = await response.json() + const generatedPrompt = data?.choices?.[0]?.message?.content || data?.message?.content || "" + + if (generatedPrompt) { + setPrompt(generatedPrompt) + showToastNotification({ + title: "Prompt Generated", + message: "AI has generated a system prompt for your agent. Review and edit as needed.", + variant: "success", + duration: 5000, + }) + } else { + throw new Error("No prompt content in response") + } + } catch (error) { + log.error("Failed to generate agent prompt", error) + showToastNotification({ + title: "Generation Failed", + message: "Could not generate prompt. Please write one manually or check your Z.AI configuration.", + variant: "error", + duration: 8000, + }) + } finally { + setIsGenerating(false) + } + } + + const handleSave = async () => { + if (!name().trim()) { + showToastNotification({ + title: "Name Required", + message: "Please provide a name for the agent.", + variant: "warning", + duration: 5000, + }) + return + } + + if (!prompt().trim()) { + showToastNotification({ + title: "Prompt Required", + message: "Please provide a system prompt for the agent.", + variant: "warning", + duration: 5000, + }) + return + } + + setIsSaving(true) + try { + await updateInstanceConfig(props.instanceId, (draft) => { + if (!draft.customAgents) { + draft.customAgents = [] + } + // Check for duplicate names + const existing = draft.customAgents.findIndex((a) => a.name.toLowerCase() === name().toLowerCase()) + if (existing >= 0) { + // Update existing + draft.customAgents[existing] = { + name: name().trim(), + description: description().trim() || undefined, + prompt: prompt().trim(), + } + } else { + // Add new + draft.customAgents.push({ + name: name().trim(), + description: description().trim() || undefined, + prompt: prompt().trim(), + }) + } + }) + + // Refresh agents list + await fetchAgents(props.instanceId) + + showToastNotification({ + title: "Agent Created", + message: `Custom agent "${name()}" has been saved and is ready to use.`, + variant: "success", + duration: 5000, + }) + + handleClose() + } catch (error) { + log.error("Failed to save custom agent", error) + showToastNotification({ + title: "Save Failed", + message: "Could not save the agent. Please try again.", + variant: "error", + duration: 8000, + }) + } finally { + setIsSaving(false) + } + } + + return ( + !open && handleClose()}> + + +
+ + {/* Header */} +
+
+
+ +
+
+ Create Custom Agent + + Define a new AI agent with custom behavior and expertise + +
+
+ +
+ + {/* Content */} +
+ {/* Name Input */} +
+ + setName(e.currentTarget.value)} + placeholder="e.g., React Specialist, Python Expert, Code Reviewer..." + class="w-full px-3 py-2 bg-zinc-800 border border-zinc-600 rounded-lg text-white placeholder:text-zinc-500 focus:outline-none focus:border-indigo-500 transition-colors" + /> +
+ + {/* Description Input */} +
+ + setDescription(e.currentTarget.value)} + placeholder="A few words about what this agent specializes in..." + class="w-full px-3 py-2 bg-zinc-800 border border-zinc-600 rounded-lg text-white placeholder:text-zinc-500 focus:outline-none focus:border-indigo-500 transition-colors" + /> +
+ + {/* Generation Mode Toggle */} +
+ + +
+ + {/* AI Generation Button */} + + + + + {/* Prompt Textarea */} +
+
+ + + {prompt().length.toLocaleString()} / {MAX_PROMPT_LENGTH.toLocaleString()} + +
+