From 25d908f3106bae50e5e0da188e5c4e33aded8cd2 Mon Sep 17 00:00:00 2001 From: shahzadbinshahajhan Date: Thu, 19 Feb 2026 01:35:36 +0400 Subject: [PATCH 1/6] update: prompt caching field, advanced settings tab --- frontend/src/components/agent/AdvancedTab.tsx | 225 ++++++++++++++++++ frontend/src/components/agent/GeneralTab.tsx | 22 ++ frontend/src/components/agent/types.ts | 8 + frontend/src/pages/AgentFormPage.tsx | 55 ++++- frontend/src/types/agent.types.ts | 8 + 5 files changed, 316 insertions(+), 2 deletions(-) create mode 100644 frontend/src/components/agent/AdvancedTab.tsx diff --git a/frontend/src/components/agent/AdvancedTab.tsx b/frontend/src/components/agent/AdvancedTab.tsx new file mode 100644 index 00000000..b053bd9a --- /dev/null +++ b/frontend/src/components/agent/AdvancedTab.tsx @@ -0,0 +1,225 @@ +import { FormField, FormItem, FormLabel, FormControl, FormDescription, FormMessage } from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { Switch } from '@/components/ui/switch'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { UseFormReturn } from 'react-hook-form'; +import type { AgentFormValues } from './types'; + +interface AdvancedTabProps { + form: UseFormReturn; +} + +export function AdvancedTab({ form }: AdvancedTabProps) { + return ( +
+ + + Context Settings + Configure how the agent handles conversation history and context + + + ( + + Context Strategy + + + How to handle conversation history when it exceeds the limit. 'Summarize' compresses old messages, 'FIFO' drops them. + + + + )} + /> + + ( + + Summary Ratio + + { + const value = e.target.value; + if (value === '') { + field.onChange(undefined); + } else { + const numValue = parseFloat(value); + if (!isNaN(numValue)) { + field.onChange(numValue); + } + } + }} + /> + + + Ratio of history to summarize effectively. 0.7 means 70% of oldest messages. + + + + )} + /> + + ( + + History Limit + + { + const value = e.target.value; + if (value === '') { + field.onChange(undefined); + } else { + const numValue = parseInt(value, 10); + if (!isNaN(numValue)) { + field.onChange(numValue); + } + } + }} + /> + + + Maximum number of messages to keep in active context before applying strategy. + + + + )} + /> + + ( + + Max Knowledge Tokens + + { + const value = e.target.value; + if (value === '') { + field.onChange(undefined); + } else { + const numValue = parseInt(value, 10); + if (!isNaN(numValue)) { + field.onChange(numValue); + } + } + }} + /> + + + Maximum tokens to use for injected knowledge context. + + + + )} + /> + + ( + + Max Turns + + { + const value = e.target.value; + if (value === '') { + field.onChange(undefined); + } else { + const numValue = parseInt(value, 10); + if (!isNaN(numValue)) { + field.onChange(numValue); + } + } + }} + /> + + + Maximum consecutive turns/steps the agent can take in a single run. + + + + )} + /> + + ( + +
+ Allow Conversation Data Management + + If enabled, the agent can store key-value pairs in the conversation context. + +
+ + + +
+ )} + /> + + ( + +
+ Autonaming of Conversation Title + + If enabled, the conversation title will be automatically updated based on the initial context. + +
+ + + +
+ )} + /> +
+
+
+ ); +} diff --git a/frontend/src/components/agent/GeneralTab.tsx b/frontend/src/components/agent/GeneralTab.tsx index 355b7e47..11331ed0 100644 --- a/frontend/src/components/agent/GeneralTab.tsx +++ b/frontend/src/components/agent/GeneralTab.tsx @@ -3,6 +3,7 @@ import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { Slider } from '@/components/ui/slider'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { Switch } from '@/components/ui/switch'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion'; import { UseFormReturn } from 'react-hook-form'; @@ -179,6 +180,27 @@ export function GeneralTab({ form, providers, models, watchProvider, optimizingP )} /> + + ( + +
+ Enable Prompt Caching + + Enable prompt caching to cache repeated prompt content and reduce token costs. Only works with supported providers (OpenAI, Anthropic, Bedrock, Deepseek). + +
+ + + +
+ )} + /> diff --git a/frontend/src/components/agent/types.ts b/frontend/src/components/agent/types.ts index 3ba479b5..cdb98cc0 100644 --- a/frontend/src/components/agent/types.ts +++ b/frontend/src/components/agent/types.ts @@ -13,6 +13,14 @@ export const agentFormSchema = z.object({ enable_multi_run: z.boolean(), description: z.string().optional(), instructions: z.string(), + enable_prompt_caching: z.boolean().optional(), + context_strategy: z.string().optional(), + summary_ratio: z.number().optional(), + history_limit: z.number().optional(), + max_knowledge_tokens: z.number().optional(), + max_turns: z.number().optional(), + enable_conversation_data: z.boolean().optional(), + autonaming_of_conversation_title: z.boolean().optional(), }); export type AgentFormValues = z.infer; diff --git a/frontend/src/pages/AgentFormPage.tsx b/frontend/src/pages/AgentFormPage.tsx index 2a8690a9..8578a2fd 100644 --- a/frontend/src/pages/AgentFormPage.tsx +++ b/frontend/src/pages/AgentFormPage.tsx @@ -19,6 +19,7 @@ import { GeneralTab } from '../components/agent/GeneralTab'; import { BehaviorTab } from '../components/agent/BehaviorTab'; import { TriggersTab } from '../components/agent/TriggersTab'; import { ToolsTab } from '../components/agent/ToolsTab'; +import { AdvancedTab } from '../components/agent/AdvancedTab'; import { agentFormSchema, type AgentFormValues } from '../components/agent/types'; import { syncMCPTools, getMCPServer, type MCPServerRef } from '../services/mcpApi'; import type { MCPServerDoc } from '../services/mcpApi'; @@ -34,7 +35,7 @@ export function AgentFormPage() { const tabConfig = { general: { label: 'General', - fields: ['agent_name', 'provider', 'model', 'temperature', 'top_p', 'description', 'instructions'], + fields: ['agent_name', 'provider', 'model', 'temperature', 'top_p', 'description', 'instructions', 'enable_prompt_caching'], default: true, disabled: false, }, @@ -56,6 +57,12 @@ export function AgentFormPage() { default: false, disabled: false, }, + advanced: { + label: 'Advanced Settings', + fields: ['context_strategy', 'summary_ratio', 'history_limit', 'max_knowledge_tokens', 'max_turns', 'enable_conversation_data', 'autonaming_of_conversation_title'], + default: false, + disabled: false, + }, } as const; // Extract derived values from tab config (memoized to avoid recreating on every render) @@ -147,6 +154,14 @@ export function AgentFormPage() { enable_multi_run: false, description: '', instructions: '', + enable_prompt_caching: false, + context_strategy: undefined, + summary_ratio: undefined, + history_limit: undefined, + max_knowledge_tokens: undefined, + max_turns: undefined, + enable_conversation_data: false, + autonaming_of_conversation_title: false, }, }); @@ -296,6 +311,14 @@ export function AgentFormPage() { enable_multi_run: data.enable_multi_run === 1, description: data.description || '', instructions: data.instructions || '', + enable_prompt_caching: data.enable_prompt_caching === 1, + context_strategy: data.context_strategy || undefined, + summary_ratio: data.summary_ratio !== undefined && data.summary_ratio !== null ? data.summary_ratio : undefined, + history_limit: data.history_limit !== undefined && data.history_limit !== null ? data.history_limit : undefined, + max_knowledge_tokens: data.max_knowledge_tokens !== undefined && data.max_knowledge_tokens !== null ? data.max_knowledge_tokens : undefined, + max_turns: data.max_turns !== undefined && data.max_turns !== null ? data.max_turns : undefined, + enable_conversation_data: data.enable_conversation_data === 1, + autonaming_of_conversation_title: data.autonaming_of_conversation_title === 1, }); // Track initial disabled state setInitialDisabled(data.disabled === 1); @@ -409,6 +432,14 @@ export function AgentFormPage() { enable_multi_run: values.enable_multi_run ? 1 : 0, description: values.description || '', instructions: values.instructions, + enable_prompt_caching: values.enable_prompt_caching ? 1 : 0, + context_strategy: values.context_strategy || undefined, + summary_ratio: values.summary_ratio !== undefined ? values.summary_ratio : undefined, + history_limit: values.history_limit !== undefined ? values.history_limit : undefined, + max_knowledge_tokens: values.max_knowledge_tokens !== undefined ? values.max_knowledge_tokens : undefined, + max_turns: values.max_turns !== undefined ? values.max_turns : undefined, + enable_conversation_data: values.enable_conversation_data ? 1 : 0, + autonaming_of_conversation_title: values.autonaming_of_conversation_title ? 1 : 0, // Include tools - Frappe child table format: array of objects with 'tool' field pointing to Agent Tool Function name agent_tool: selectedTools.map((tool) => ({ tool: tool.name, @@ -438,6 +469,14 @@ export function AgentFormPage() { enable_multi_run: newAgent.enable_multi_run === 1, description: newAgent.description || '', instructions: newAgent.instructions || '', + enable_prompt_caching: newAgent.enable_prompt_caching === 1, + context_strategy: newAgent.context_strategy || undefined, + summary_ratio: newAgent.summary_ratio !== undefined && newAgent.summary_ratio !== null ? newAgent.summary_ratio : undefined, + history_limit: newAgent.history_limit !== undefined && newAgent.history_limit !== null ? newAgent.history_limit : undefined, + max_knowledge_tokens: newAgent.max_knowledge_tokens !== undefined && newAgent.max_knowledge_tokens !== null ? newAgent.max_knowledge_tokens : undefined, + max_turns: newAgent.max_turns !== undefined && newAgent.max_turns !== null ? newAgent.max_turns : undefined, + enable_conversation_data: newAgent.enable_conversation_data === 1, + autonaming_of_conversation_title: newAgent.autonaming_of_conversation_title === 1, }); setInitialDisabled(newAgent.disabled === 1); // Navigate to the edit page with the new agent's ID @@ -460,6 +499,14 @@ export function AgentFormPage() { enable_multi_run: values.enable_multi_run, description: values.description, instructions: values.instructions, + enable_prompt_caching: values.enable_prompt_caching, + context_strategy: values.context_strategy, + summary_ratio: values.summary_ratio, + history_limit: values.history_limit, + max_knowledge_tokens: values.max_knowledge_tokens, + max_turns: values.max_turns, + enable_conversation_data: values.enable_conversation_data, + autonaming_of_conversation_title: values.autonaming_of_conversation_title, }); // Reset tools, disabled state, and MCP servers after successful update to mark as unchanged setInitialTools([...selectedTools]); @@ -800,7 +847,7 @@ export function AgentFormPage() {
- + {Object.entries(tabConfig).map(([tabKey, config]) => ( {config.label} @@ -852,6 +899,10 @@ export function AgentFormPage() { mcpLoading={mcpLoading} /> + + + +
diff --git a/frontend/src/types/agent.types.ts b/frontend/src/types/agent.types.ts index 83c1615e..a7520f0e 100644 --- a/frontend/src/types/agent.types.ts +++ b/frontend/src/types/agent.types.ts @@ -199,4 +199,12 @@ export interface AgentDoc { last_run?: string | null; // Last execution timestamp total_run?: number; // Total number of runs agent_color?: string | null; // Hex color code for agent background + enable_prompt_caching?: number; // 0 or 1 + context_strategy?: string | null; // Summarize, FIFO, or None + summary_ratio?: number | null; // Ratio of history to summarize (0-1) + history_limit?: number | null; // Maximum number of messages to keep + max_knowledge_tokens?: number | null; // Maximum tokens for knowledge context + max_turns?: number | null; // Maximum consecutive turns/steps + enable_conversation_data?: number; // 0 or 1 + autonaming_of_conversation_title?: number; // 0 or 1 } From 7d26600b1c7b4b8b20b627f58dece4696ec2abf2 Mon Sep 17 00:00:00 2001 From: shahzadbinshahajhan Date: Thu, 19 Feb 2026 02:16:05 +0400 Subject: [PATCH 2/6] feat: tool creation --- .../src/components/tools/HttpHeaderCard.tsx | 69 ++ .../src/components/tools/ParameterCard.tsx | 161 +++++ .../src/components/tools/SelectToolsModal.tsx | 269 +++++--- .../src/components/tools/ToolCreationForm.tsx | 646 ++++++++++++++++++ .../src/components/tools/ToolTemplateCard.tsx | 43 ++ frontend/src/config/toolTemplates.json | 69 ++ frontend/src/services/toolApi.ts | 88 +++ frontend/src/types/agent.types.ts | 4 + frontend/src/types/toolTemplate.types.ts | 41 ++ 9 files changed, 1311 insertions(+), 79 deletions(-) create mode 100644 frontend/src/components/tools/HttpHeaderCard.tsx create mode 100644 frontend/src/components/tools/ParameterCard.tsx create mode 100644 frontend/src/components/tools/ToolCreationForm.tsx create mode 100644 frontend/src/components/tools/ToolTemplateCard.tsx create mode 100644 frontend/src/config/toolTemplates.json create mode 100644 frontend/src/types/toolTemplate.types.ts diff --git a/frontend/src/components/tools/HttpHeaderCard.tsx b/frontend/src/components/tools/HttpHeaderCard.tsx new file mode 100644 index 00000000..4afba625 --- /dev/null +++ b/frontend/src/components/tools/HttpHeaderCard.tsx @@ -0,0 +1,69 @@ +import { Trash2 } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Card, CardContent } from '@/components/ui/card'; +import { Label } from '@/components/ui/label'; + +export type HttpHeaderData = { + key: string; + value: string; +}; + +interface HttpHeaderCardProps { + header: HttpHeaderData; + index: number; + onChange: (index: number, data: Partial) => void; + onDelete: (index: number) => void; +} + +export function HttpHeaderCard({ + header, + index, + onChange, + onDelete, +}: HttpHeaderCardProps) { + const handleChange = (field: keyof HttpHeaderData, value: string) => { + onChange(index, { [field]: value }); + }; + + return ( + + +
+

Header {index + 1}

+ +
+ +
+
+ + handleChange('key', e.target.value)} + placeholder="e.g., Authorization" + /> +
+ +
+ + handleChange('value', e.target.value)} + placeholder="e.g., Bearer token123" + /> +
+
+
+
+ ); +} diff --git a/frontend/src/components/tools/ParameterCard.tsx b/frontend/src/components/tools/ParameterCard.tsx new file mode 100644 index 00000000..be64f232 --- /dev/null +++ b/frontend/src/components/tools/ParameterCard.tsx @@ -0,0 +1,161 @@ +import { Trash2 } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Textarea } from '@/components/ui/textarea'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { Checkbox } from '@/components/ui/checkbox'; +import { Card, CardContent } from '@/components/ui/card'; +import { Label } from '@/components/ui/label'; + +export type ParameterData = { + label: string; + fieldname: string; + type: 'string' | 'integer' | 'number' | 'float' | 'boolean' | 'object' | 'array'; + required: boolean; + description?: string; + options?: string; + child_table_name?: string; +}; + +interface ParameterCardProps { + parameter: ParameterData; + index: number; + onChange: (index: number, data: Partial) => void; + onDelete: (index: number) => void; +} + +const parameterTypes = [ + 'string', + 'integer', + 'number', + 'float', + 'boolean', + 'object', + 'array', +] as const; + +export function ParameterCard({ + parameter, + index, + onChange, + onDelete, +}: ParameterCardProps) { + const handleChange = (field: keyof ParameterData, value: any) => { + onChange(index, { [field]: value }); + }; + + return ( + + +
+

Parameter {index + 1}

+ +
+ +
+
+ + handleChange('label', e.target.value)} + placeholder="e.g., Document ID" + /> +
+ +
+ + handleChange('fieldname', e.target.value)} + placeholder="e.g., document_id" + /> +
+
+ +
+
+ + +
+ +
+ + handleChange('child_table_name', e.target.value)} + placeholder="Optional" + /> +
+
+ +
+ +