AncViz's picture
Can you add working example files (so when you lick 'load example' on the examples tab, it loads the real example)? This may require adjusting the functionality of the drag-and-drop components
72e5643 verified
// Shared JavaScript across all pages
class AgentBuilder {
constructor() {
this.components = [];
this.connections = [];
this.selectedComponent = null;
this.dragging = false;
this.dragOffset = { x: 0, y: 0 };
this.initializeEventListeners();
}
initializeEventListeners() {
const canvas = document.getElementById('canvas');
// Drag start from toolbox
document.querySelectorAll('.toolbox-item').forEach(item => {
item.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('text/plain', e.target.closest('.toolbox-item').dataset.type);
});
});
// Drop on canvas
canvas.addEventListener('dragover', (e) => {
e.preventDefault();
canvas.classList.add('border-blue-500');
});
canvas.addEventListener('dragleave', () => {
canvas.classList.remove('border-blue-500');
});
canvas.addEventListener('drop', (e) => {
e.preventDefault();
canvas.classList.remove('border-blue-500');
const type = e.dataTransfer.getData('text/plain');
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
this.addComponent(type, x, y);
});
// Canvas interactions
canvas.addEventListener('mousedown', (e) => {
const component = e.target.closest('.canvas-component');
if (component) {
this.selectComponent(component);
if (e.target.classList.contains('component-handle')) {
this.startDragging(component, e);
}
} else {
this.deselectComponent();
}
});
document.addEventListener('mousemove', (e) => {
if (this.dragging) {
this.dragComponent(e);
}
});
document.addEventListener('mouseup', () => {
this.dragging = false;
});
}
addComponent(type, x, y) {
const id = `comp-${Date.now()}`;
const component = {
id,
type,
x,
y,
properties: this.getDefaultProperties(type)
};
const element = document.createElement('div');
element.className = 'canvas-component';
element.id = id;
element.style.left = `${x}px`;
element.style.top = `${y}px`;
element.innerHTML = this.getComponentHTML(type, id);
document.getElementById('canvas').appendChild(element);
this.components.push(component);
// Add event listeners to the new component
element.addEventListener('dblclick', () => this.showProperties(component));
this.addConnectionPoints(element, id);
}
getComponentHTML(type, id) {
const templates = {
'react-agent': `
<div class="component-handle flex items-center justify-between">
<i data-feather="cpu" class="w-4 h-4"></i>
<span class="text-sm font-semibold">React Agent</span>
</div>
<div class="text-xs text-gray-300">Reasoning engine</div>
`,
'tool': `
<div class="component-handle flex items-center justify-between">
<i data-feather="tool" class="w-4 h-4"></i>
<span class="text-sm font-semibold">Tool</span>
</div>
<div class="text-xs text-gray-300">Function tool</div>
`,
'prompt': `
<div class="component-handle flex items-center justify-between">
<i data-feather="message-square" class="w-4 h-4"></i>
<span class="text-sm font-semibold">Prompt Template</span>
</div>
<div class="text-xs text-gray-300">Chat setup</div>
`,
'memory': `
<div class="component-handle flex items-center justify-between">
<i data-feather="database" class="w-4 h-4"></i>
<span class="text-sm font-semibold">Memory</span>
</div>
<div class="text-xs text-gray-300">State storage</div>
`
};
return templates[type] || '<div>Unknown Component</div>';
}
addConnectionPoints(element, id) {
const inputPoint = document.createElement('div');
inputPoint.className = 'connection-point input';
inputPoint.dataset.component = id;
inputPoint.dataset.type = 'input';
const outputPoint = document.createElement('div');
outputPoint.className = 'connection-point output';
outputPoint.dataset.component = id;
outputPoint.dataset.type = 'output';
element.appendChild(inputPoint);
element.appendChild(outputPoint);
}
getDefaultProperties(type) {
const defaults = {
'react-agent': {
name: 'react_agent',
model: 'gpt-4',
temperature: 0.7
},
'tool': {
name: 'custom_tool',
description: 'A custom function tool',
function: 'def tool_function(input):\n return "processed: " + input'
},
'prompt': {
template: 'You are a helpful assistant. Answer the following question: {question}',
variables: ['question']
},
'memory': {
type: 'MemorySaver',
checkpoint_ttl: 3600
}
};
return defaults[type] || {};
}
selectComponent(componentElement) {
this.deselectComponent();
componentElement.classList.add('selected');
this.selectedComponent = this.components.find(c => c.id === componentElement.id);
this.showProperties(this.selectedComponent);
}
deselectComponent() {
document.querySelectorAll('.canvas-component.selected').forEach(el => {
el.classList.remove('selected');
});
this.selectedComponent = null;
this.hideProperties();
}
startDragging(componentElement, e) {
this.dragging = true;
const rect = componentElement.getBoundingClientRect();
const canvasRect = document.getElementById('canvas').getBoundingClientRect();
this.dragOffset = {
x: e.clientX - rect.left + canvasRect.left,
y: e.clientY - rect.top + canvasRect.top
};
}
dragComponent(e) {
if (!this.selectedComponent) return;
const canvasRect = document.getElementById('canvas').getBoundingClientRect();
const x = e.clientX - this.dragOffset.x;
const y = e.clientY - this.dragOffset.y;
const componentElement = document.getElementById(this.selectedComponent.id);
componentElement.style.left = `${x}px`;
componentElement.style.top = `${y}px`;
this.selectedComponent.x = x;
this.selectedComponent.y = y;
}
showProperties(component) {
const panel = document.getElementById('properties-panel');
const content = document.getElementById('properties-content');
panel.classList.remove('hidden');
content.innerHTML = this.generatePropertiesForm(component);
// Update properties on input change
content.querySelectorAll('input, textarea').forEach(input => {
input.addEventListener('change', (e) => {
component.properties[e.target.name] = e.target.value;
});
});
}
hideProperties() {
document.getElementById('properties-panel').classList.add('hidden');
}
generatePropertiesForm(component) {
const properties = component.properties;
let html = `<h3 class="text-lg font-semibold mb-4 capitalize">${component.type.replace('-', ' ')} Properties</h3>`;
Object.keys(properties).forEach(key => {
const value = properties[key];
const isTextarea = typeof value === 'string' && value.includes('\n');
html += `
<div class="property-group">
<label class="property-label capitalize">${key.replace('_', ' ')}</label>
${isTextarea ?
`<textarea name="${key}" class="property-input h-24">${value}</textarea>` :
`<input type="text" name="${key}" value="${value}" class="property-input">`
}
</div>
`;
});
return html;
}
exportToPython() {
let code = `# Generated by AgentFlow Studio\n`;
code += `from langchain_core.messages import HumanMessage, ToolMessage\n`;
code += `from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n`;
code += `from langgraph.graph import StateGraph, END\n`;
code += `from langgraph.checkpoint.memory import MemorySaver\n`;
code += `from pydantic import BaseModel, Field\n`;
code += `from langchain.agents import create_react_agent\n\n`;
// Add component definitions
this.components.forEach(comp => {
code += `# ${comp.type} component\n`;
switch(comp.type) {
case 'react-agent':
code += `agent = create_react_agent(\n`;
code += ` model="${comp.properties.model}",\n`;
code += ` temperature=${comp.properties.temperature}\n`;
code += `)\n\n`;
break;
case 'tool':
code += `class ${comp.properties.name.replace('_', ' ').title().replace(' ', '')}Tool(BaseModel):\n`;
code += ` \"\"\"${comp.properties.description}\"\"\"\n\n`;
code += ` def execute(self, input: str) -> str:\n`;
code += ` ${comp.properties.function.replace('\n', '\n ')}\n\n`;
break;
case 'prompt':
code += `prompt_template = ChatPromptTemplate.from_template(\n`;
code += ` \"\"\"${comp.properties.template}\"\"\"\n`;
code += `)\n\n`;
break;
case 'memory':
code += `memory = MemorySaver()\n\n`;
break;
}
});
// Add graph construction
code += `# Build the agent graph\n`;
code += `graph_builder = StateGraph()\n\n`;
code += `# Export the final agent\n`;
code += `def get_agent():\n`;
code += ` return graph_builder.compile()\n`;
return code;
}
}
// Global functions for UI interactions
function startNewProject() {
const builder = new AgentBuilder();
window.agentBuilder = builder;
document.getElementById('canvas').innerHTML = '';
document.getElementById('properties-panel').classList.add('hidden');
}
function loadSample() {
startNewProject();
// Add sample components
window.agentBuilder.addComponent('react-agent', 100, 100);
window.agentBuilder.addComponent('tool', 300, 100);
window.agentBuilder.addComponent('prompt', 100, 200);
window.agentBuilder.addComponent('memory', 300, 200);
}
function loadExample(exampleType) {
startNewProject();
switch(exampleType) {
case 'research':
// Research Assistant Example
const researchAgent = window.agentBuilder.addComponent('react-agent', 100, 100);
const webSearchTool = window.agentBuilder.addComponent('tool', 300, 100);
const researchPrompt = window.agentBuilder.addComponent('prompt', 100, 200);
const researchMemory = window.agentBuilder.addComponent('memory', 300, 200);
// Configure components
const researchComponents = window.agentBuilder.components;
researchComponents.find(c => c.id === researchAgent).properties = {
name: 'research_assistant',
model: 'gpt-4',
temperature: 0.3,
max_tokens: 2000
};
researchComponents.find(c => c.id === webSearchTool).properties = {
name: 'web_search',
description: 'Search the web for current information',
function: 'def web_search(query):\n # Implementation for web search\n return f"Searched for: {query}"'
};
researchComponents.find(c => c.id === researchPrompt).properties = {
template: 'You are a research assistant. Search for information about: {topic}. Provide detailed, well-sourced answers.',
variables: ['topic']
};
researchComponents.find(c => c.id === researchMemory).properties = {
type: 'MemorySaver',
checkpoint_ttl: 7200
};
break;
case 'ecommerce':
// E-commerce Assistant Example
const ecomAgent = window.agentBuilder.addComponent('react-agent', 100, 100);
const productSearch = window.agentBuilder.addComponent('tool', 300, 100);
const priceCompare = window.agentBuilder.addComponent('tool', 500, 100);
const ecomPrompt = window.agentBuilder.addComponent('prompt', 100, 200);
const ecomMemory = window.agentBuilder.addComponent('memory', 300, 200);
const ecomComponents = window.agentBuilder.components;
ecomComponents.find(c => c.id === ecomAgent).properties = {
name: 'ecommerce_assistant',
model: 'gpt-4',
temperature: 0.2,
max_tokens: 1500
};
ecomComponents.find(c => c.id === productSearch).properties = {
name: 'product_search',
description: 'Search for products in the catalog',
function: 'def product_search(category, keywords):\n # Search product database\n return f"Products in {category} matching {keywords}"'
};
ecomComponents.find(c => c.id === priceCompare).properties = {
name: 'price_comparison',
description: 'Compare prices across different vendors',
function: 'def price_comparison(product_id):\n # Get price comparisons\n return f"Price comparison for product {product_id}"'
};
ecomComponents.find(c => c.id === ecomPrompt).properties = {
template: 'You are an e-commerce assistant. Help customers find products and compare prices. Be helpful and concise.',
variables: ['user_query']
};
ecomComponents.find(c => c.id === ecomMemory).properties = {
type: 'MemorySaver',
checkpoint_ttl: 3600
};
break;
case 'code':
// Code Assistant Example
const codeAgent = window.agentBuilder.addComponent('react-agent', 100, 100);
const codeGenerator = window.agentBuilder.addComponent('tool', 300, 100);
const debuggerTool = window.agentBuilder.addComponent('tool', 500, 100);
const codePrompt = window.agentBuilder.addComponent('prompt', 100, 200);
const codeMemory = window.agentBuilder.addComponent('memory', 300, 200);
const codeComponents = window.agentBuilder.components;
codeComponents.find(c => c.id === codeAgent).properties = {
name: 'code_assistant',
model: 'gpt-4',
temperature: 0.1,
max_tokens: 3000
};
codeComponents.find(c => c.id === codeGenerator).properties = {
name: 'code_generation',
description: 'Generate code based on requirements',
function: 'def generate_code(language, requirements):\n # Generate code implementation\n return f"Generated {language} code for: {requirements}"'
};
codeComponents.find(c => c.id === debuggerTool).properties = {
name: 'debug_code',
description: 'Help debug and fix code issues',
function: 'def debug_code(code, error):\n # Debug code implementation\n return f"Debugged code with error: {error}"'
};
codeComponents.find(c => c.id === codePrompt).properties = {
template: 'You are a programming assistant. Help with code generation, debugging, and explanations. Provide clear, working code examples.',
variables: ['programming_task']
};
codeComponents.find(c => c.id === codeMemory).properties = {
type: 'MemorySaver',
checkpoint_ttl: 4800
};
break;
case 'support':
// Customer Support Example
const supportAgent = window.agentBuilder.addComponent('react-agent', 100, 100);
const faqSearch = window.agentBuilder.addComponent('tool', 300, 100);
const ticketCreator = window.agentBuilder.addComponent('tool', 500, 100);
const supportPrompt = window.agentBuilder.addComponent('prompt', 100, 200);
const supportMemory = window.agentBuilder.addComponent('memory', 300, 200);
const supportComponents = window.agentBuilder.components;
supportComponents.find(c => c.id === supportAgent).properties = {
name: 'support_agent',
model: 'gpt-4',
temperature: 0.4,
max_tokens: 1200
};
supportComponents.find(c => c.id === faqSearch).properties = {
name: 'faq_search',
description: 'Search the FAQ database for answers',
function: 'def search_faq(question):\n # Search FAQ database\n return f"FAQ results for: {question}"'
};
supportComponents.find(c => c.id === ticketCreator).properties = {
name: 'create_ticket',
description: 'Create a support ticket for escalation',
function: 'def create_ticket(issue, priority):\n # Create support ticket\n return f"Created {priority} ticket for: {issue}"'
};
supportComponents.find(c => c.id === supportPrompt).properties = {
template: 'You are a customer support agent. Be empathetic and helpful. Use FAQs when possible, escalate when needed.',
variables: ['customer_issue']
};
supportComponents.find(c => c.id === supportMemory).properties = {
type: 'MemorySaver',
checkpoint_ttl: 2400
};
break;
case 'analyst':
// Data Analyst Example
const analystAgent = window.agentBuilder.addComponent('react-agent', 100, 100);
const dataProcessor = window.agentBuilder.addComponent('tool', 300, 100);
const statsAnalyzer = window.agentBuilder.addComponent('tool', 500, 100);
const analystPrompt = window.agentBuilder.addComponent('prompt', 100, 200);
const analystMemory = window.agentBuilder.addComponent('memory', 300, 200);
const analystComponents = window.agentBuilder.components;
analystComponents.find(c => c.id === analystAgent).properties = {
name: 'data_analyst',
model: 'gpt-4',
temperature: 0.2,
max_tokens: 2500
};
analystComponents.find(c => c.id === dataProcessor).properties = {
name: 'process_data',
description: 'Process and clean datasets',
function: 'def process_data(dataset, operations):\n # Process data implementation\n return f"Processed dataset with: {operations}"'
};
analystComponents.find(c => c.id === statsAnalyzer).properties = {
name: 'analyze_statistics',
description: 'Perform statistical analysis on data',
function: 'def analyze_stats(data, metrics):\n # Statistical analysis\n return f"Analysis results for metrics: {metrics}"'
};
analystComponents.find(c => c.id === analystPrompt).properties = {
template: 'You are a data analyst. Help analyze datasets, generate insights, and create visualizations. Be precise and data-driven.',
variables: ['analysis_request']
};
analystComponents.find(c => c.id === analystMemory).properties = {
type: 'MemorySaver',
checkpoint_ttl: 6000
};
break;
case 'translator':
// Multi-language Translator Example
const translatorAgent = window.agentBuilder.addComponent('react-agent', 100, 100);
const translateTool = window.agentBuilder.addComponent('tool', 300, 100);
const contextTool = window.agentBuilder.addComponent('tool', 500, 100);
const translatorPrompt = window.agentBuilder.addComponent('prompt', 100, 200);
const translatorMemory = window.agentBuilder.addComponent('memory', 300, 200);
const translatorComponents = window.agentBuilder.components;
translatorComponents.find(c => c.id === translatorAgent).properties = {
name: 'translator',
model: 'gpt-4',
temperature: 0.3,
max_tokens: 1800
};
translatorComponents.find(c => c.id === translateTool).properties = {
name: 'translate_text',
description: 'Translate text between languages',
function: 'def translate(text, source_lang, target_lang):\n # Translation implementation\n return f"Translated from {source_lang} to {target_lang}: {text}"'
};
translatorComponents.find(c => c.id === contextTool).properties = {
name: 'preserve_context',
description: 'Maintain context and cultural nuances',
function: 'def preserve_context(translation, context):\n # Context preservation\n return f"Context-preserved translation: {translation}"'
};
translatorComponents.find(c => c.id === translatorPrompt).properties = {
template: 'You are a multilingual translator. Translate text while preserving meaning, context, and cultural nuances. Support multiple languages.',
variables: ['text_to_translate', 'target_language']
};
translatorComponents.find(c => c.id === translatorMemory).properties = {
type: 'MemorySaver',
checkpoint_ttl: 5400
};
break;
}
// Update the UI to show the loaded example
document.getElementById('properties-panel').classList.add('hidden');
// Show success message
showNotification(`Loaded ${exampleType.replace(/_/g, ' ')} example successfully!`, 'success');
}
function exportAgent() {
if (!window.agentBuilder || window.agentBuilder.components.length === 0) {
showNotification('Please add some components to the canvas first!', 'error');
return;
}
const code = window.agentBuilder.exportToPython();
showCodeModal(code);
}
function clearCanvas() {
if (confirm('Are you sure you want to clear the canvas?')) {
document.getElementById('canvas').innerHTML = '';
document.getElementById('properties-panel').classList.add('hidden');
if (window.agentBuilder) {
window.agentBuilder.components = [];
window.agentBuilder.connections = [];
}
showNotification('Canvas cleared successfully!', 'success');
}
}
function showCodeModal(code) {
const modal = document.createElement('div');
modal.className = 'modal-overlay';
modal.innerHTML = `
<div class="modal-content">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-semibold">Generated Python Code</h3>
<button onclick="this.closest('.modal-overlay').remove()" class="text-gray-400 hover:text-white">
<i data-feather="x"></i>
</button>
</div>
<pre class="bg-gray-900 p-4 rounded-lg overflow-auto max-h-96"><code class="language-python">${code}</code></pre>
<div class="flex gap-2 mt-4">
<button onclick="copyCode()" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-lg flex-1">
<i data-feather="copy" class="mr-2"></i>Copy to Clipboard
</button>
<button onclick="downloadCode()" class="bg-green-600 hover:bg-green-700 px-4 py-2 rounded-lg flex-1">
<i data-feather="download" class="mr-2"></i>Download .py
</button>
</div>
</div>
`;
document.body.appendChild(modal);
feather.replace();
window.copyCode = function() {
navigator.clipboard.writeText(code).then(() => {
alert('Code copied to clipboard!');
});
};
window.downloadCode = function() {
const blob = new Blob([code], { type: 'text/python' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'langchain_agent.py';
a.click();
URL.revokeObjectURL(url);
};
}
// Notification system
function showNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.className = `fixed top-4 right-4 z-50 p-4 rounded-lg shadow-lg transition-all duration-300 transform translate-x-full ${
type === 'success' ? 'bg-green-600' :
type === 'error' ? 'bg-red-600' :
'bg-blue-600'
} text-white`;
notification.innerHTML = `
<div class="flex items-center gap-2">
<i data-feather="${type === 'success' ? 'check-circle' : type === 'error' ? 'alert-circle' : 'info'}"></i>
<span>${message}</span>
</div>
`;
document.body.appendChild(notification);
// Animate in
setTimeout(() => {
notification.style.transform = 'translateX(0)';
}, 100);
// Animate out and remove
setTimeout(() => {
notification.style.transform = 'translateX(full)';
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 300);
}, 3000);
feather.replace();
}
// Initialize the app when page loads
document.addEventListener('DOMContentLoaded', () => {
window.agentBuilder = new AgentBuilder();
feather.replace();
// Check for example parameter in URL
const urlParams = new URLSearchParams(window.location.search);
const example = urlParams.get('example');
if (example && ['research', 'ecommerce', 'code', 'support', 'analyst', 'translator'].includes(example)) {
// Small delay to ensure everything is loaded
setTimeout(() => {
loadExample(example);
}, 500);
}
});
console.log('AgentFlow Studio loaded');