262 lines
9.0 KiB
React
262 lines
9.0 KiB
React
import React, { useState, useEffect } from 'react';
|
||
import axios from 'axios';
|
||
import './ToolsPage.css';
|
||
|
||
const ToolsPage = () => {
|
||
const [toolExecutions, setToolExecutions] = useState([]);
|
||
const [selectedTool, setSelectedTool] = useState('');
|
||
const [toolInput, setToolInput] = useState('');
|
||
const [executing, setExecuting] = useState(false);
|
||
const [message, setMessage] = useState('');
|
||
|
||
const tools = [
|
||
{
|
||
id: 'query_expander',
|
||
name: 'Query Expander',
|
||
description: 'Expands and refines engineering queries for better understanding',
|
||
icon: '🔍',
|
||
color: 'blue'
|
||
},
|
||
{
|
||
id: 'extraction',
|
||
name: 'Document Extraction',
|
||
description: 'Extracts relevant information from uploaded documents',
|
||
icon: '📄',
|
||
color: 'green'
|
||
},
|
||
{
|
||
id: 'report1',
|
||
name: 'Report Generator',
|
||
description: 'Generates structured engineering reports',
|
||
icon: '📊',
|
||
color: 'purple'
|
||
},
|
||
{
|
||
id: 'report2',
|
||
name: 'File Generator',
|
||
description: 'Creates downloadable engineering files',
|
||
icon: '📁',
|
||
color: 'orange'
|
||
},
|
||
{
|
||
id: 'web_search',
|
||
name: 'Web Search',
|
||
description: 'Searches the web for current engineering information',
|
||
icon: '🌐',
|
||
color: 'cyan'
|
||
},
|
||
{
|
||
id: 'encyclopedia_pdf',
|
||
name: 'Encyclopedia Search',
|
||
description: 'Searches internal PDF knowledge base',
|
||
icon: '📚',
|
||
color: 'red'
|
||
}
|
||
];
|
||
|
||
useEffect(() => {
|
||
loadToolExecutions();
|
||
}, []);
|
||
|
||
const loadToolExecutions = async () => {
|
||
try {
|
||
const response = await axios.get('/api/tools/executions');
|
||
setToolExecutions(response.data.data.executions || []);
|
||
} catch (error) {
|
||
console.error('Error loading tool executions:', error);
|
||
}
|
||
};
|
||
|
||
const executeTool = async () => {
|
||
setMessage('⚠️ **Manual tool execution is disabled.** Tools can only be executed as part of an approved engineering plan. Please use the Chat page to create and execute plans.');
|
||
setSelectedTool('');
|
||
setToolInput('');
|
||
};
|
||
|
||
const retryExecution = async (executionId) => {
|
||
try {
|
||
await axios.post(`/api/tools/executions/${executionId}/retry`);
|
||
setMessage('✅ Tool execution retried');
|
||
loadToolExecutions();
|
||
} catch (error) {
|
||
console.error('Retry error:', error);
|
||
setMessage(`❌ Retry failed: ${error.response?.data?.error || error.message}`);
|
||
}
|
||
};
|
||
|
||
const getStatusColor = (status) => {
|
||
switch (status) {
|
||
case 'completed': return 'success';
|
||
case 'failed': return 'error';
|
||
case 'running': return 'warning';
|
||
default: return 'info';
|
||
}
|
||
};
|
||
|
||
const formatDate = (dateString) => {
|
||
return new Date(dateString).toLocaleString();
|
||
};
|
||
|
||
return (
|
||
<div className="tools-page">
|
||
<div className="page-header">
|
||
<h1>🔧 Engineering Tools</h1>
|
||
<p>View tool execution history and results from approved engineering plans</p>
|
||
</div>
|
||
|
||
{message && (
|
||
<div className={`message ${message.includes('✅') ? 'success' : message.includes('⚠️') ? 'warning' : 'error'}`}>
|
||
{message}
|
||
</div>
|
||
)}
|
||
|
||
{/* Tool Selection */}
|
||
<div className="tool-selection">
|
||
<div className="section-header">
|
||
<h2>🛠️ Tool Information</h2>
|
||
<p>Available engineering tools (execution requires approved plan)</p>
|
||
</div>
|
||
|
||
<div className="tool-selector">
|
||
<div className="tool-grid">
|
||
{tools.map((tool) => (
|
||
<button
|
||
key={tool.id}
|
||
className={`tool-card ${selectedTool === tool.id ? 'selected' : ''} ${tool.color}`}
|
||
onClick={() => setSelectedTool(tool.id)}
|
||
>
|
||
<div className="tool-icon">{tool.icon}</div>
|
||
<div className="tool-info">
|
||
<h3>{tool.name}</h3>
|
||
<p>{tool.description}</p>
|
||
</div>
|
||
</button>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{selectedTool && (
|
||
<div className="execution-panel">
|
||
<div className="panel-header">
|
||
<h3>{tools.find(t => t.id === selectedTool)?.name} Information</h3>
|
||
</div>
|
||
<div className="panel-content">
|
||
<div className="tool-info">
|
||
<p><strong>Description:</strong> {tools.find(t => t.id === selectedTool)?.description}</p>
|
||
<p><strong>Usage:</strong> This tool is automatically executed as part of approved engineering plans.</p>
|
||
<p><strong>To use this tool:</strong></p>
|
||
<ol>
|
||
<li>Go to the Chat page</li>
|
||
<li>Ask an engineering question</li>
|
||
<li>Review the generated plan</li>
|
||
<li>Approve the plan</li>
|
||
<li>The system will automatically execute the appropriate tools</li>
|
||
</ol>
|
||
</div>
|
||
<div className="execution-actions">
|
||
<button
|
||
className="execute-btn disabled"
|
||
onClick={executeTool}
|
||
disabled={true}
|
||
>
|
||
🔒 Manual Execution Disabled
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Execution History */}
|
||
<div className="execution-history">
|
||
<div className="section-header">
|
||
<h2>📋 Execution History</h2>
|
||
<p>View past tool executions and their results</p>
|
||
</div>
|
||
|
||
<div className="history-container">
|
||
{toolExecutions.length > 0 ? (
|
||
<div className="executions-list">
|
||
{toolExecutions.map((execution) => (
|
||
<div key={execution.id} className="execution-card">
|
||
<div className="execution-header">
|
||
<div className="execution-info">
|
||
<h4>
|
||
{tools.find(t => t.id === execution.tool_name)?.icon}
|
||
{tools.find(t => t.id === execution.tool_name)?.name || execution.tool_name}
|
||
</h4>
|
||
<div className="execution-meta">
|
||
<span className="execution-id">ID: {execution.id}</span>
|
||
<span className="execution-time">{formatDate(execution.created_at)}</span>
|
||
</div>
|
||
</div>
|
||
<div className="execution-status">
|
||
<span className={`status-badge ${getStatusColor(execution.status)}`}>
|
||
{execution.status}
|
||
</span>
|
||
{execution.status === 'failed' && (
|
||
<button
|
||
className="retry-btn"
|
||
onClick={() => retryExecution(execution.id)}
|
||
>
|
||
🔄 Retry
|
||
</button>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="execution-details">
|
||
<div className="detail-section">
|
||
<h5>Input Parameters</h5>
|
||
<pre className="code-block">
|
||
{JSON.stringify(execution.input_parameters, null, 2)}
|
||
</pre>
|
||
</div>
|
||
|
||
{execution.output_result && (
|
||
<div className="detail-section">
|
||
<h5>Output Result</h5>
|
||
<pre className="code-block">
|
||
{JSON.stringify(execution.output_result, null, 2)}
|
||
</pre>
|
||
</div>
|
||
)}
|
||
|
||
{execution.error_message && (
|
||
<div className="detail-section error">
|
||
<h5>Error Message</h5>
|
||
<pre className="error-block">
|
||
{execution.error_message}
|
||
</pre>
|
||
</div>
|
||
)}
|
||
|
||
<div className="execution-metrics">
|
||
<div className="metric">
|
||
<span className="metric-label">Duration:</span>
|
||
<span className="metric-value">{execution.duration || 'N/A'}ms</span>
|
||
</div>
|
||
<div className="metric">
|
||
<span className="metric-label">Tokens Used:</span>
|
||
<span className="metric-value">{execution.tokens_used || 'N/A'}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
) : (
|
||
<div className="empty-state">
|
||
<div className="empty-icon">🔧</div>
|
||
<h3>No tool executions yet</h3>
|
||
<p>Execute your first tool to see the results here</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default ToolsPage;
|