feat(frontend): implement agent configuration pages (#41)

- Add agent types list page with search and filter functionality
- Add agent type detail/edit page with tabbed interface
- Create AgentTypeForm component with React Hook Form + Zod validation
- Implement model configuration (temperature, max tokens, top_p)
- Add MCP permission management with checkboxes
- Include personality prompt editor textarea
- Create TanStack Query hooks for agent-types API
- Add useDebounce hook for search optimization
- Comprehensive unit tests for all components (68 tests)

Components:
- AgentTypeList: Grid view with status badges, expertise tags
- AgentTypeDetail: Full detail view with model config, MCP permissions
- AgentTypeForm: Create/edit with 4 tabs (Basic, Model, Permissions, Personality)

Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-30 23:48:49 +01:00
parent e85788f79f
commit 68f1865a1e
17 changed files with 2888 additions and 0 deletions

View File

@@ -0,0 +1,155 @@
/**
* Agent Type Form Validation Schemas
*
* Zod schemas for validating agent type form data.
* Used with react-hook-form for form validation.
*/
import { z } from 'zod';
/**
* Slug validation regex: lowercase letters, numbers, and hyphens only
*/
const slugRegex = /^[a-z0-9-]+$/;
/**
* Available AI models for agent types
*/
export const AVAILABLE_MODELS = [
{ value: 'claude-opus-4-5-20251101', label: 'Claude Opus 4.5' },
{ value: 'claude-sonnet-4-20250514', label: 'Claude Sonnet 4' },
{ value: 'claude-3-5-sonnet-20241022', label: 'Claude 3.5 Sonnet' },
] as const;
/**
* Available MCP servers for agent permissions
*/
export const AVAILABLE_MCP_SERVERS = [
{ id: 'gitea', name: 'Gitea', description: 'Git repository management' },
{ id: 'knowledge', name: 'Knowledge Base', description: 'Vector database for RAG' },
{ id: 'filesystem', name: 'Filesystem', description: 'File read/write operations' },
{ id: 'slack', name: 'Slack', description: 'Team communication' },
{ id: 'browser', name: 'Browser', description: 'Web browsing and scraping' },
] as const;
/**
* Agent type status options
*/
export const AGENT_TYPE_STATUS = [
{ value: true, label: 'Active' },
{ value: false, label: 'Inactive' },
] as const;
/**
* Model params schema
*/
const modelParamsSchema = z.object({
temperature: z.number().min(0).max(2),
max_tokens: z.number().int().min(1024).max(128000),
top_p: z.number().min(0).max(1),
});
/**
* Schema for agent type form fields
*/
export const agentTypeFormSchema = z.object({
name: z
.string()
.min(1, 'Name is required')
.max(255, 'Name must be less than 255 characters'),
slug: z
.string()
.min(1, 'Slug is required')
.max(255, 'Slug must be less than 255 characters')
.regex(slugRegex, 'Slug must contain only lowercase letters, numbers, and hyphens')
.refine((val) => !val.startsWith('-') && !val.endsWith('-'), {
message: 'Slug cannot start or end with a hyphen',
})
.refine((val) => !val.includes('--'), {
message: 'Slug cannot contain consecutive hyphens',
}),
description: z
.string()
.max(2000, 'Description must be less than 2000 characters')
.nullable()
.optional(),
expertise: z.array(z.string()),
personality_prompt: z
.string()
.min(1, 'Personality prompt is required')
.max(10000, 'Personality prompt must be less than 10000 characters'),
primary_model: z.string().min(1, 'Primary model is required'),
fallback_models: z.array(z.string()),
model_params: modelParamsSchema,
mcp_servers: z.array(z.string()),
tool_permissions: z.record(z.string(), z.unknown()),
is_active: z.boolean(),
});
/**
* Schema for creating a new agent type (alias for backward compatibility)
*/
export const agentTypeCreateSchema = agentTypeFormSchema;
/**
* Schema for updating an existing agent type
* All fields are optional since we support partial updates
*/
export const agentTypeUpdateSchema = agentTypeFormSchema.partial();
/**
* Type for agent type create form values
*/
export type AgentTypeCreateFormValues = z.infer<typeof agentTypeCreateSchema>;
/**
* Type for agent type update form values
*/
export type AgentTypeUpdateFormValues = z.infer<typeof agentTypeUpdateSchema>;
/**
* Default values for creating a new agent type
*/
export const defaultAgentTypeValues: AgentTypeCreateFormValues = {
name: '',
slug: '',
description: null,
expertise: [],
personality_prompt: '',
primary_model: 'claude-opus-4-5-20251101',
fallback_models: ['claude-sonnet-4-20250514'],
model_params: {
temperature: 0.7,
max_tokens: 8192,
top_p: 0.95,
},
mcp_servers: [],
tool_permissions: {},
is_active: false, // Start as draft
};
/**
* Generate slug from name
*
* @param name - The name to convert to a slug
* @returns A valid slug string
*/
export function generateSlug(name: string): string {
return name
.toLowerCase()
.trim()
.replace(/[^a-z0-9\s-]/g, '') // Remove special characters
.replace(/\s+/g, '-') // Replace spaces with hyphens
.replace(/-+/g, '-') // Replace multiple hyphens with single
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
}