/* ┌──────────────────────────────────────────────────────────────────────────────┐ │ @author: Davidson Gomes │ │ @file: /app/documentation/components/HttpLabForm.tsx │ │ Developed by: Davidson Gomes │ │ Creation date: May 13, 2025 │ │ Contact: contato@evolution-api.com │ ├──────────────────────────────────────────────────────────────────────────────┤ │ @copyright © Evolution API 2025. All rights reserved. │ │ Licensed under the Apache License, Version 2.0 │ │ │ │ You may not use this file except in compliance with the License. │ │ You may obtain a copy of the License at │ │ │ │ http://www.apache.org/licenses/LICENSE-2.0 │ │ │ │ Unless required by applicable law or agreed to in writing, software │ │ distributed under the License is distributed on an "AS IS" BASIS, │ │ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ │ See the License for the specific language governing permissions and │ │ limitations under the License. │ ├──────────────────────────────────────────────────────────────────────────────┤ │ @important │ │ For any future changes to the code in this file, it is recommended to │ │ include, together with the modification, the information of the developer │ │ who changed it and the date of modification. │ └──────────────────────────────────────────────────────────────────────────────┘ */ import { useRef, useState } from "react"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Button } from "@/components/ui/button"; import { Separator } from "@/components/ui/separator"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Send, Paperclip, X, FileText, Image, File, RotateCcw, Trash2 } from "lucide-react"; import { toast } from "@/hooks/use-toast"; interface AttachedFile { name: string; type: string; size: number; base64: string; } interface HttpLabFormProps { agentUrl: string; setAgentUrl: (url: string) => void; apiKey: string; setApiKey: (key: string) => void; message: string; setMessage: (message: string) => void; sessionId: string; setSessionId: (id: string) => void; taskId: string; setTaskId: (id: string) => void; callId: string; setCallId: (id: string) => void; sendRequest: () => Promise; isLoading: boolean; setFiles?: (files: AttachedFile[]) => void; a2aMethod: string; setA2aMethod: (method: string) => void; authMethod: string; setAuthMethod: (method: string) => void; generateNewIds: () => void; currentTaskId?: string | null; conversationHistory?: any[]; clearHistory?: () => void; webhookUrl?: string; setWebhookUrl?: (url: string) => void; enableWebhooks?: boolean; setEnableWebhooks?: (enabled: boolean) => void; showDetailedErrors?: boolean; setShowDetailedErrors?: (show: boolean) => void; } export function HttpLabForm({ agentUrl, setAgentUrl, apiKey, setApiKey, message, setMessage, sessionId, setSessionId, taskId, setTaskId, callId, setCallId, sendRequest, isLoading, setFiles = () => {}, a2aMethod, setA2aMethod, authMethod, setAuthMethod, generateNewIds, currentTaskId, conversationHistory, clearHistory, webhookUrl, setWebhookUrl, enableWebhooks, setEnableWebhooks, showDetailedErrors, setShowDetailedErrors }: HttpLabFormProps) { const [attachedFiles, setAttachedFiles] = useState([]); const fileInputRef = useRef(null); const clearAttachedFiles = () => { setAttachedFiles([]); }; const handleSendRequest = async () => { await sendRequest(); clearAttachedFiles(); }; const handleFileSelect = async (e: React.ChangeEvent) => { if (!e.target.files || e.target.files.length === 0) return; const maxFileSize = 5 * 1024 * 1024; // 5MB limit const newFiles = Array.from(e.target.files); if (attachedFiles.length + newFiles.length > 5) { toast({ title: "File limit exceeded", description: "You can only attach up to 5 files.", variant: "destructive" }); return; } const filesToAdd: AttachedFile[] = []; for (const file of newFiles) { if (file.size > maxFileSize) { toast({ title: "File too large", description: `The file ${file.name} exceeds the 5MB size limit.`, variant: "destructive" }); continue; } try { const base64 = await readFileAsBase64(file); filesToAdd.push({ name: file.name, type: file.type, size: file.size, base64: base64 }); } catch (error) { console.error("Failed to read file:", error); toast({ title: "Failed to read file", description: `Could not process ${file.name}.`, variant: "destructive" }); } } if (filesToAdd.length > 0) { const updatedFiles = [...attachedFiles, ...filesToAdd]; setAttachedFiles(updatedFiles); setFiles(updatedFiles); } // Reset file input if (fileInputRef.current) { fileInputRef.current.value = ""; } }; const readFileAsBase64 = (file: File): Promise => { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => { const result = reader.result as string; const base64 = result.split(',')[1]; // Remove data URL prefix resolve(base64); }; reader.onerror = reject; reader.readAsDataURL(file); }); }; const removeFile = (index: number) => { const updatedFiles = attachedFiles.filter((_, i) => i !== index); setAttachedFiles(updatedFiles); setFiles(updatedFiles); }; const formatFileSize = (bytes: number): string => { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; }; const isImageFile = (type: string): boolean => { return type.startsWith('image/'); }; return (
{/* A2A Method and Authentication Settings */}
{/* Multi-turn Conversation History Controls */} {(a2aMethod === "message/send" || a2aMethod === "message/stream") && conversationHistory && conversationHistory.length > 0 && (
Multi-turn Conversation Active
💬 {conversationHistory.length} messages in conversation history (contextId active)
)} {/* Push Notifications (Webhook) Configuration */} {(a2aMethod === "message/send" || a2aMethod === "message/stream" || a2aMethod.startsWith("tasks/")) && (
setEnableWebhooks?.(e.target.checked)} className="rounded bg-[#222] border-[#444] text-blue-400 focus:ring-blue-400" />
{enableWebhooks && (
setWebhookUrl?.(e.target.value)} placeholder="https://your-server.com/webhook/a2a" className="bg-[#222] border-[#444] text-white" />
{a2aMethod === "tasks/pushNotificationConfig/set" ? "📡 Configure push notifications for task" : "📡 Webhook URL for push notifications (configured via pushNotificationConfig)" }
)} {!enableWebhooks && (
{a2aMethod === "tasks/pushNotificationConfig/set" ? "Push notification configuration will be set to null." : "No push notifications will be configured for this request." }
)}
)} {/* Advanced Error Handling Configuration */}
setShowDetailedErrors?.(e.target.checked)} className="rounded bg-[#222] border-[#444] text-orange-400 focus:ring-orange-400" />
{showDetailedErrors ? "🔍 Detailed error information will be shown in debug logs (client-side only)." : "⚡ Basic error handling only - minimal error information in logs." }
setAgentUrl(e.target.value)} placeholder="http://localhost:8000/api/v1/a2a/your-agent-id" className="bg-[#222] border-[#444] text-white" />
setApiKey(e.target.value)} placeholder={authMethod === "bearer" ? "Your Bearer token" : "Your API key"} className="bg-[#222] border-[#444] text-white" />
{/* Show current task ID if available */} {currentTaskId && (
Current Task ID: {currentTaskId}
)} {/* Message input - only show for message methods */} {(a2aMethod === "message/send" || a2aMethod === "message/stream") && (