diff --git a/services/actions/search.go b/services/actions/search.go index ed09ac1..ed5c4dc 100644 --- a/services/actions/search.go +++ b/services/actions/search.go @@ -100,7 +100,7 @@ func SearchConfigMeta() []config.Field { Type: config.FieldTypeNumber, DefaultValue: 1, Min: 1, - Max: 10, + Max: 100, Step: 1, HelpText: "Number of search results to return", }, diff --git a/webui/react-ui/src/components/ActionForm.jsx b/webui/react-ui/src/components/ActionForm.jsx index 1976bb0..bb3b389 100644 --- a/webui/react-ui/src/components/ActionForm.jsx +++ b/webui/react-ui/src/components/ActionForm.jsx @@ -6,12 +6,7 @@ import ConfigForm from './ConfigForm'; * Renders action configuration forms based on field group metadata */ const ActionForm = ({ actions = [], onChange, onRemove, onAdd, fieldGroups = [] }) => { - // Debug logging - console.log('ActionForm:', { actions, fieldGroups }); - - // Handle action change const handleActionChange = (index, updatedAction) => { - console.log('Action change:', { index, updatedAction }); onChange(index, updatedAction); }; diff --git a/webui/react-ui/src/components/AgentForm.jsx b/webui/react-ui/src/components/AgentForm.jsx index 1c601b5..ac9afcf 100644 --- a/webui/react-ui/src/components/AgentForm.jsx +++ b/webui/react-ui/src/components/AgentForm.jsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import { useNavigate, useOutletContext } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; // Import form sections import BasicInfoSection from './agent-form-sections/BasicInfoSection'; @@ -10,6 +10,7 @@ import MemorySettingsSection from './agent-form-sections/MemorySettingsSection'; import ModelSettingsSection from './agent-form-sections/ModelSettingsSection'; import PromptsGoalsSection from './agent-form-sections/PromptsGoalsSection'; import AdvancedSettingsSection from './agent-form-sections/AdvancedSettingsSection'; +import ExportSection from './agent-form-sections/ExportSection'; const AgentForm = ({ isEdit = false, @@ -20,10 +21,9 @@ const AgentForm = ({ submitButtonText, isGroupForm = false, noFormWrapper = false, - metadata = null + metadata = null, }) => { const navigate = useNavigate(); - const { showToast } = useOutletContext(); const [activeSection, setActiveSection] = useState(isGroupForm ? 'model-section' : 'basic-section'); // Handle input changes @@ -57,16 +57,27 @@ const AgentForm = ({ // Handle navigation between sections const handleSectionChange = (section) => { + console.log('Changing section to:', section); setActiveSection(section); }; + // Handle connector change (simplified) + const handleConnectorChange = (index, updatedConnector) => { + const updatedConnectors = [...formData.connectors]; + updatedConnectors[index] = updatedConnector; + setFormData({ + ...formData, + connectors: updatedConnectors + }); + }; + // Handle adding a connector const handleAddConnector = () => { setFormData({ ...formData, connectors: [ ...(formData.connectors || []), - { name: '', config: '{}' } + { type: '', config: '{}' } ] }); }; @@ -81,55 +92,6 @@ const AgentForm = ({ }); }; - // Handle connector name change - const handleConnectorNameChange = (index, value) => { - const updatedConnectors = [...formData.connectors]; - updatedConnectors[index] = { - ...updatedConnectors[index], - type: value - }; - setFormData({ - ...formData, - connectors: updatedConnectors - }); - }; - - // Handle connector config change - const handleConnectorConfigChange = (index, key, value) => { - const updatedConnectors = [...formData.connectors]; - const currentConnector = updatedConnectors[index]; - - // Parse the current config if it's a string - let currentConfig = {}; - if (typeof currentConnector.config === 'string') { - try { - currentConfig = JSON.parse(currentConnector.config); - } catch (err) { - console.error('Error parsing config:', err); - currentConfig = {}; - } - } else if (currentConnector.config) { - currentConfig = currentConnector.config; - } - - // Update the config with the new key-value pair - currentConfig = { - ...currentConfig, - [key]: value - }; - - // Update the connector with the stringified config - updatedConnectors[index] = { - ...currentConnector, - config: JSON.stringify(currentConfig) - }; - - setFormData({ - ...formData, - connectors: updatedConnectors - }); - }; - // Handle adding an MCP server const handleAddMCPServer = () => { setFormData({ @@ -231,6 +193,17 @@ const AgentForm = ({ Advanced Settings + {isEdit && ( + <> +
  • handleSectionChange('export-section')} + > + + Export Data +
  • + + )} @@ -248,7 +221,7 @@ const AgentForm = ({
    - +
    @@ -270,6 +243,14 @@ const AgentForm = ({
    + + {isEdit && ( + <> +
    + +
    + + )}
    ) : (
    @@ -283,7 +264,7 @@ const AgentForm = ({
    - +
    @@ -306,13 +287,21 @@ const AgentForm = ({
    + {isEdit && ( + <> +
    + +
    + + )} + {/* Form Controls */} -
    - -
    diff --git a/webui/react-ui/src/components/ConfigForm.jsx b/webui/react-ui/src/components/ConfigForm.jsx index 934cbdf..392fe11 100644 --- a/webui/react-ui/src/components/ConfigForm.jsx +++ b/webui/react-ui/src/components/ConfigForm.jsx @@ -24,9 +24,6 @@ const ConfigForm = ({ typeField = 'type', addButtonText = 'Add Item' }) => { - // Debug logging - console.log(`ConfigForm for ${itemType}:`, { items, fieldGroups }); - // Generate options from fieldGroups const typeOptions = [ { value: '', label: `Select a ${itemType} type` }, @@ -35,8 +32,6 @@ const ConfigForm = ({ label: group.label })) ]; - - console.log(`${itemType} type options:`, typeOptions); // Parse the config JSON string to an object const parseConfig = (item) => { @@ -82,15 +77,13 @@ const ConfigForm = ({ // Find the field group that matches this item's type const fieldGroup = fieldGroups.find(group => group.name === itemTypeName); - console.log(`Item ${index} type: ${itemTypeName}, Found field group:`, fieldGroup); - return (

    {itemType.charAt(0).toUpperCase() + itemType.slice(1)} #{index + 1}

    diff --git a/webui/react-ui/src/components/agent-form-sections/ExportSection.jsx b/webui/react-ui/src/components/agent-form-sections/ExportSection.jsx new file mode 100644 index 0000000..72feb1c --- /dev/null +++ b/webui/react-ui/src/components/agent-form-sections/ExportSection.jsx @@ -0,0 +1,28 @@ +import React, { useEffect } from 'react'; + +const ExportSection = ({ agentName }) => { + useEffect(() => { + console.log('ExportSection rendered with agentName:', agentName); + }, [agentName]); + + return ( +
    +
    +

    Export Data

    +
    + +
    +

    Export your agent configuration for backup or transfer.

    + + Export Configuration + +
    +
    + ); +}; + +export default ExportSection; diff --git a/webui/react-ui/src/components/agent-form-sections/MCPServersSection.jsx b/webui/react-ui/src/components/agent-form-sections/MCPServersSection.jsx index 5fd07fa..48b5c3d 100644 --- a/webui/react-ui/src/components/agent-form-sections/MCPServersSection.jsx +++ b/webui/react-ui/src/components/agent-form-sections/MCPServersSection.jsx @@ -46,7 +46,7 @@ const MCPServersSection = ({

    MCP Server #{index + 1}

    ))} diff --git a/webui/react-ui/src/hooks/useAgent.js b/webui/react-ui/src/hooks/useAgent.js index 2a211e2..f0d5f36 100644 --- a/webui/react-ui/src/hooks/useAgent.js +++ b/webui/react-ui/src/hooks/useAgent.js @@ -19,8 +19,21 @@ export function useAgent(agentName) { setError(null); try { + // Fetch the agent configuration const config = await agentApi.getAgentConfig(agentName); - setAgent(config); + + // Fetch the agent status + const response = await fetch(`/api/agent/${agentName}`); + if (!response.ok) { + throw new Error(`Failed to fetch agent status: ${response.status}`); + } + const statusData = await response.json(); + + // Combine configuration with active status + setAgent({ + ...config, + active: statusData.active + }); } catch (err) { setError(err.message || 'Failed to fetch agent configuration'); console.error('Error fetching agent:', err); @@ -63,8 +76,13 @@ export function useAgent(agentName) { } else { await agentApi.startAgent(agentName); } - // Refresh agent data after status change - await fetchAgent(); + + // Update the agent's active status in the local state + setAgent(prevAgent => ({ + ...prevAgent, + active: !isActive + })); + return true; } catch (err) { setError(err.message || 'Failed to toggle agent status'); @@ -73,7 +91,7 @@ export function useAgent(agentName) { } finally { setLoading(false); } - }, [agentName, fetchAgent]); + }, [agentName]); // Delete agent const deleteAgent = useCallback(async () => { diff --git a/webui/react-ui/src/pages/AgentsList.jsx b/webui/react-ui/src/pages/AgentsList.jsx index 92f5baa..5189282 100644 --- a/webui/react-ui/src/pages/AgentsList.jsx +++ b/webui/react-ui/src/pages/AgentsList.jsx @@ -13,29 +13,14 @@ function AgentsList() { const fetchAgents = async () => { setLoading(true); try { - const response = await fetch('/agents'); - const html = await response.text(); + const response = await fetch('/api/agents'); + if (!response.ok) { + throw new Error(`Server responded with status: ${response.status}`); + } - // Create a temporary element to parse the HTML - const tempDiv = document.createElement('div'); - tempDiv.innerHTML = html; - - // Extract agent names and statuses from the HTML - const agentElements = tempDiv.querySelectorAll('[data-agent]'); - const agentList = []; - const statusMap = {}; - - agentElements.forEach(el => { - const name = el.getAttribute('data-agent'); - const status = el.getAttribute('data-active') === 'true'; - if (name) { - agentList.push(name); - statusMap[name] = status; - } - }); - - setAgents(agentList); - setStatuses(statusMap); + const data = await response.json(); + setAgents(data.agents || []); + setStatuses(data.statuses || {}); } catch (err) { console.error('Error fetching agents:', err); setError('Failed to load agents'); @@ -47,7 +32,7 @@ function AgentsList() { // Toggle agent status (pause/start) const toggleAgentStatus = async (name, isActive) => { try { - const endpoint = isActive ? `/pause/${name}` : `/start/${name}`; + const endpoint = isActive ? `/api/agent/${name}/pause` : `/api/agent/${name}/start`; const response = await fetch(endpoint, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, @@ -64,8 +49,12 @@ function AgentsList() { // Show success toast const action = isActive ? 'paused' : 'started'; showToast(`Agent "${name}" ${action} successfully`, 'success'); + + // Refresh the agents list to ensure we have the latest data + fetchAgents(); } else { - throw new Error(`Server responded with status: ${response.status}`); + const errorData = await response.json().catch(() => null); + throw new Error(errorData?.error || `Server responded with status: ${response.status}`); } } catch (err) { console.error(`Error toggling agent status:`, err); @@ -80,7 +69,7 @@ function AgentsList() { } try { - const response = await fetch(`/delete/${name}`, { + const response = await fetch(`/api/agent/${name}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, }); @@ -88,11 +77,17 @@ function AgentsList() { if (response.ok) { // Remove from local state setAgents(prev => prev.filter(agent => agent !== name)); + setStatuses(prev => { + const newStatuses = { ...prev }; + delete newStatuses[name]; + return newStatuses; + }); // Show success toast showToast(`Agent "${name}" deleted successfully`, 'success'); } else { - throw new Error(`Server responded with status: ${response.status}`); + const errorData = await response.json().catch(() => null); + throw new Error(errorData?.error || `Server responded with status: ${response.status}`); } } catch (err) { console.error(`Error deleting agent:`, err); @@ -117,7 +112,7 @@ function AgentsList() {

    Manage Agents

    - + Create New Agent
    @@ -198,8 +193,8 @@ function AgentsList() {

    No Agents Found

    Get started by creating your first agent

    - - Create Agent + + Create Agent
    )} diff --git a/webui/react-ui/src/pages/GroupCreate.jsx b/webui/react-ui/src/pages/GroupCreate.jsx index 61548bd..41c44f9 100644 --- a/webui/react-ui/src/pages/GroupCreate.jsx +++ b/webui/react-ui/src/pages/GroupCreate.jsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { useNavigate, useOutletContext } from 'react-router-dom'; import { agentApi } from '../utils/api'; import AgentForm from '../components/AgentForm'; @@ -10,6 +10,7 @@ function GroupCreate() { const [generatingProfiles, setGeneratingProfiles] = useState(false); const [activeStep, setActiveStep] = useState(1); const [selectedProfiles, setSelectedProfiles] = useState([]); + const [metadata, setMetadata] = useState(null); const [formData, setFormData] = useState({ description: '', model: '', @@ -20,6 +21,24 @@ function GroupCreate() { profiles: [] }); + // Fetch metadata on component mount + useEffect(() => { + const fetchMetadata = async () => { + try { + // Fetch metadata from the dedicated endpoint + const response = await agentApi.getAgentConfigMetadata(); + if (response) { + setMetadata(response); + } + } catch (error) { + console.error('Error fetching metadata:', error); + // Continue without metadata, the form will use default fields + } + }; + + fetchMetadata(); + }, []); + // Handle form field changes const handleInputChange = (e) => { const { name, value, type } = e.target; @@ -281,6 +300,7 @@ function GroupCreate() { submitButtonText="Create Group" isGroupForm={true} noFormWrapper={true} + metadata={metadata} />
    diff --git a/webui/react-ui/src/pages/Home.jsx b/webui/react-ui/src/pages/Home.jsx index eb7f6e6..81440b0 100644 --- a/webui/react-ui/src/pages/Home.jsx +++ b/webui/react-ui/src/pages/Home.jsx @@ -20,11 +20,11 @@ function Home() { try { const agents = await agentApi.getAgents(); setStats({ - agents: agents.Agents || [], - agentCount: agents.AgentCount || 0, - actions: agents.Actions || 0, - connectors: agents.Connectors || 0, - status: agents.Status || {}, + agents: agents.agents || [], + agentCount: agents.agentCount || 0, + actions: agents.actions || 0, + connectors: agents.connectors || 0, + status: agents.statuses || {}, }); } catch (err) { console.error('Error fetching dashboard data:', err); @@ -115,14 +115,14 @@ function Home() {

    {agent}

    - - Chat + + Chat - - Settings + + Settings - - Status + + Status
    diff --git a/webui/react-ui/src/utils/config.js b/webui/react-ui/src/utils/config.js index dfbadbe..30698d8 100644 --- a/webui/react-ui/src/utils/config.js +++ b/webui/react-ui/src/utils/config.js @@ -20,11 +20,11 @@ export const API_CONFIG = { // Agent endpoints agents: '/api/agents', agentConfig: (name) => `/api/agent/${name}/config`, - agentConfigMetadata: '/api/agent/config/metadata', + agentConfigMetadata: '/api/meta/agent/config', createAgent: '/create', - deleteAgent: (name) => `/delete/${name}`, - pauseAgent: (name) => `/pause/${name}`, - startAgent: (name) => `/start/${name}`, + deleteAgent: (name) => `/api/agent/${name}`, + pauseAgent: (name) => `/api/agent/${name}/pause`, + startAgent: (name) => `/api/agent/${name}/start`, exportAgent: (name) => `/settings/export/${name}`, importAgent: '/settings/import', diff --git a/webui/routes.go b/webui/routes.go index 03685da..18af783 100644 --- a/webui/routes.go +++ b/webui/routes.go @@ -130,6 +130,11 @@ func (app *App) registerRoutes(pool *state.AgentPool, webapp *fiber.App) { webapp.Put("/pause/:name", app.Pause(pool)) webapp.Put("/start/:name", app.Start(pool)) + // JSON API endpoints for agent operations + webapp.Delete("/api/agent/:name", app.Delete(pool)) + webapp.Put("/api/agent/:name/pause", app.Pause(pool)) + webapp.Put("/api/agent/:name/start", app.Start(pool)) + webapp.Post("/v1/responses", app.Responses(pool)) webapp.Get("/talk/:name", func(c *fiber.Ctx) error { @@ -171,9 +176,9 @@ func (app *App) registerRoutes(pool *state.AgentPool, webapp *fiber.App) { // New API endpoints for getting and updating agent configuration webapp.Get("/api/agent/:name/config", app.GetAgentConfig(pool)) webapp.Put("/api/agent/:name/config", app.UpdateAgentConfig(pool)) - - // Metadata endpoint for agent configuration fields - webapp.Get("/api/agent/config/metadata", app.GetAgentConfigMeta()) + + // Add endpoint for getting agent config metadata + webapp.Get("/api/meta/agent/config", app.GetAgentConfigMeta()) webapp.Post("/action/:name/run", app.ExecuteAction(pool)) webapp.Get("/actions", app.ListActions()) @@ -195,11 +200,27 @@ func (app *App) registerRoutes(pool *state.AgentPool, webapp *fiber.App) { } return c.JSON(fiber.Map{ - "Agents": agents, - "AgentCount": len(agents), - "Actions": len(services.AvailableActions), - "Connectors": len(services.AvailableConnectors), - "Status": statuses, + "agents": agents, + "agentCount": len(agents), + "actions": len(services.AvailableActions), + "connectors": len(services.AvailableConnectors), + "statuses": statuses, + }) + }) + + // API endpoint for getting a specific agent's details + webapp.Get("/api/agent/:name", func(c *fiber.Ctx) error { + name := c.Params("name") + agent := pool.GetAgent(name) + if agent == nil { + return c.Status(fiber.StatusNotFound).JSON(fiber.Map{ + "error": "Agent not found", + }) + } + + // Add the active status to the configuration + return c.JSON(fiber.Map{ + "active": !agent.Paused(), }) })