/** * Dynamic Model Loader - Frontend Logic * سیستم هوشمند بارگذاری مدل - منطق فرانت‌اند */ const dynamicLoader = { apiBase: window.location.origin, registeredModels: [], /** * مقداردهی اولیه */ async init() { console.log('🚀 Initializing Dynamic Model Loader...'); // Load registered models await this.refreshModelsList(); // Setup event listeners this.setupEventListeners(); console.log('✅ Dynamic Model Loader initialized'); }, setupEventListeners() { // Manual form submission const manualForm = document.getElementById('manual-form'); if (manualForm) { manualForm.addEventListener('submit', async (e) => { e.preventDefault(); await this.submitManualConfig(); }); } }, /** * نمایش حالت‌های مختلف */ showPasteMode() { this.closeAllModes(); document.getElementById('paste-mode').style.display = 'block'; document.getElementById('paste-input').focus(); }, showManualMode() { this.closeAllModes(); document.getElementById('manual-mode').style.display = 'block'; document.getElementById('manual-model-id').focus(); }, showAutoMode() { this.closeAllModes(); document.getElementById('auto-mode').style.display = 'block'; document.getElementById('auto-url').focus(); }, closeAllModes() { document.getElementById('paste-mode').style.display = 'none'; document.getElementById('manual-mode').style.display = 'none'; document.getElementById('auto-mode').style.display = 'none'; }, closeTestPanel() { document.getElementById('test-panel').style.display = 'none'; }, /** * پردازش کپی/پیست */ async processPastedConfig() { const configText = document.getElementById('paste-input').value.trim(); const autoDetect = document.getElementById('auto-detect-paste').checked; if (!configText) { this.showError('Please paste a configuration'); return; } this.showInfo('Processing pasted configuration...'); try { const response = await fetch(`${this.apiBase}/api/dynamic-models/paste-config`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ config_text: configText, auto_detect: autoDetect }) }); const data = await response.json(); if (data.success) { this.showSuccess(`Model "${data.data.model_id}" registered successfully!`); await this.refreshModelsList(); this.closeAllModes(); document.getElementById('paste-input').value = ''; } else { this.showError(data.error || 'Failed to process configuration'); } } catch (error) { this.showError(`Error: ${error.message}`); console.error('Paste config error:', error); } }, async testPastedConfig() { const configText = document.getElementById('paste-input').value.trim(); if (!configText) { this.showError('Please paste a configuration'); return; } this.showInfo('Testing configuration...'); try { // Parse the config let parsedConfig; try { parsedConfig = JSON.parse(configText); } catch { this.showError('Invalid JSON. Please provide valid JSON configuration for testing.'); return; } const response = await fetch(`${this.apiBase}/api/dynamic-models/test-connection`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(parsedConfig) }); const data = await response.json(); if (data.success && data.test_result.success) { this.showSuccess(`✅ Connection successful! (${Math.round(data.test_result.response_time_ms)}ms)`); } else { this.showError(`❌ Connection failed: ${data.test_result.error || 'Unknown error'}`); } } catch (error) { this.showError(`Test failed: ${error.message}`); console.error('Test error:', error); } }, /** * ارسال فرم دستی */ async submitManualConfig() { const config = { model_id: document.getElementById('manual-model-id').value.trim(), model_name: document.getElementById('manual-model-name').value.trim(), base_url: document.getElementById('manual-base-url').value.trim(), api_key: document.getElementById('manual-api-key').value.trim() || null, api_type: document.getElementById('manual-api-type').value === 'auto' ? null : document.getElementById('manual-api-type').value, endpoints: document.getElementById('manual-endpoint').value.trim() || null }; const testFirst = document.getElementById('test-before-register').checked; if (testFirst) { this.showInfo('Testing connection first...'); try { const testResponse = await fetch(`${this.apiBase}/api/dynamic-models/test-connection`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(config) }); const testData = await testResponse.json(); if (!testData.success || !testData.test_result.success) { const proceed = confirm( `Connection test failed: ${testData.test_result.error}\n\nDo you want to register anyway?` ); if (!proceed) return; } } catch (error) { const proceed = confirm( `Test failed: ${error.message}\n\nDo you want to register anyway?` ); if (!proceed) return; } } this.showInfo('Registering model...'); try { const response = await fetch(`${this.apiBase}/api/dynamic-models/register`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(config) }); const data = await response.json(); if (data.success) { this.showSuccess(`Model "${config.model_id}" registered successfully!`); await this.refreshModelsList(); this.closeAllModes(); document.getElementById('manual-form').reset(); } else { this.showError(data.message || 'Registration failed'); } } catch (error) { this.showError(`Error: ${error.message}`); console.error('Registration error:', error); } }, async testManualConfig() { const config = { model_id: document.getElementById('manual-model-id').value.trim(), model_name: document.getElementById('manual-model-name').value.trim(), base_url: document.getElementById('manual-base-url').value.trim(), api_key: document.getElementById('manual-api-key').value.trim() || null, api_type: document.getElementById('manual-api-type').value === 'auto' ? null : document.getElementById('manual-api-type').value }; if (!config.base_url) { this.showError('Please enter a base URL'); return; } this.showInfo('Testing connection...'); try { const response = await fetch(`${this.apiBase}/api/dynamic-models/test-connection`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(config) }); const data = await response.json(); if (data.success && data.test_result.success) { this.showSuccess( `✅ Connection successful!\n` + `API Type: ${data.test_result.api_type}\n` + `Response Time: ${Math.round(data.test_result.response_time_ms)}ms\n` + `Capabilities: ${data.test_result.detected_capabilities.join(', ')}` ); } else { this.showError( `❌ Connection failed:\n${data.test_result.error || 'Unknown error'}` ); } } catch (error) { this.showError(`Test failed: ${error.message}`); console.error('Test error:', error); } }, /** * تنظیم خودکار از URL */ async autoConfigureFromURL() { const url = document.getElementById('auto-url').value.trim(); if (!url) { this.showError('Please enter a URL'); return; } this.showInfo('Auto-configuring model...'); try { const response = await fetch(`${this.apiBase}/api/dynamic-models/auto-configure`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url }) }); const data = await response.json(); if (data.success) { this.showSuccess( `✅ Model auto-configured and registered!\n` + `Model ID: ${data.config.model_id}\n` + `API Type: ${data.config.api_type}\n` + `Endpoints discovered: ${Object.keys(data.config.endpoints?.endpoints || {}).length}` ); await this.refreshModelsList(); this.closeAllModes(); document.getElementById('auto-url').value = ''; } else { this.showError(data.error || 'Auto-configuration failed'); } } catch (error) { this.showError(`Error: ${error.message}`); console.error('Auto-configure error:', error); } }, /** * بازخوانی لیست مدل‌ها */ async refreshModelsList() { const container = document.getElementById('models-list'); try { const response = await fetch(`${this.apiBase}/api/dynamic-models/models`); const data = await response.json(); if (data.success) { this.registeredModels = data.models; this.renderModelsList(data.models); } else { container.innerHTML = '

Failed to load models

'; } } catch (error) { console.error('Failed to load models:', error); container.innerHTML = '

Error loading models

'; } }, renderModelsList(models) { const container = document.getElementById('models-list'); if (models.length === 0) { container.innerHTML = `

No models registered yet

Click one of the quick action buttons above to register your first model

`; return; } container.innerHTML = models.map(model => `

${this.escapeHtml(model.model_name)}

${model.api_type || 'unknown'}
ID: ${this.escapeHtml(model.model_id)}
URL: ${this.escapeHtml(model.base_url)}
${model.api_key ? '
Auth: Yes (API key set)
' : ''}
Created: ${new Date(model.created_at).toLocaleString()} ${model.last_used_at ? `Last used: ${new Date(model.last_used_at).toLocaleString()}` : ''} Uses: ${model.use_count || 0}
`).join(''); }, /** * عملیات روی مدل‌ها */ openTestModel(modelId) { // Populate test panel const select = document.getElementById('test-model-select'); select.innerHTML = this.registeredModels.map(m => `` ).join(''); // Show test panel document.getElementById('test-panel').style.display = 'block'; document.getElementById('test-panel').scrollIntoView({ behavior: 'smooth' }); }, async executeTest() { const modelId = document.getElementById('test-model-select').value; const endpoint = document.getElementById('test-endpoint').value.trim(); const payloadText = document.getElementById('test-payload').value.trim(); if (!modelId) { this.showError('Please select a model'); return; } let payload; try { payload = JSON.parse(payloadText || '{}'); } catch { this.showError('Invalid JSON payload'); return; } this.showInfo('Testing model...'); const resultDiv = document.getElementById('test-result'); resultDiv.innerHTML = '

Running test...

'; try { const response = await fetch( `${this.apiBase}/api/dynamic-models/models/${modelId}/use`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ endpoint, payload }) } ); const data = await response.json(); if (data.success) { this.showSuccess(`Test completed in ${Math.round(data.data.response_time_ms)}ms`); resultDiv.innerHTML = `
✅ Test Successful
Response Time: ${Math.round(data.data.response_time_ms)}ms
Response Data:
${JSON.stringify(data.data.data, null, 2)}
`; } else { this.showError('Test failed'); resultDiv.innerHTML = `
❌ Test Failed
Error: ${data.error}
`; } } catch (error) { this.showError(`Test error: ${error.message}`); resultDiv.innerHTML = `
❌ Error
${error.message}
`; } }, viewModelDetails(modelId) { const model = this.registeredModels.find(m => m.model_id === modelId); if (!model) return; alert(` Model Details: -------------- ID: ${model.model_id} Name: ${model.model_name} API Type: ${model.api_type} Base URL: ${model.base_url} Created: ${new Date(model.created_at).toLocaleString()} Use Count: ${model.use_count || 0} Auto-detected: ${model.auto_detected ? 'Yes' : 'No'} Config: ${JSON.stringify(model.config, null, 2)} Endpoints: ${JSON.stringify(model.endpoints, null, 2)} `.trim()); }, async deleteModel(modelId) { if (!confirm(`Are you sure you want to delete model "${modelId}"?`)) { return; } try { const response = await fetch( `${this.apiBase}/api/dynamic-models/models/${modelId}`, { method: 'DELETE' } ); const data = await response.json(); if (data.success) { this.showSuccess(`Model "${modelId}" deleted`); await this.refreshModelsList(); } else { this.showError('Failed to delete model'); } } catch (error) { this.showError(`Error: ${error.message}`); } }, /** * پیغام‌های وضعیت */ showSuccess(message) { this.showMessage(message, 'success'); }, showError(message) { this.showMessage(message, 'error'); }, showInfo(message) { this.showMessage(message, 'info'); }, showMessage(message, type = 'info') { const container = document.getElementById('status-messages'); const messageDiv = document.createElement('div'); messageDiv.className = `status-message ${type}`; messageDiv.textContent = message; container.appendChild(messageDiv); setTimeout(() => { messageDiv.remove(); }, 5000); }, /** * ابزارها */ escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } }; // Auto-initialize when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => dynamicLoader.init()); } else { dynamicLoader.init(); } // Export for global access window.dynamicLoader = dynamicLoader;