- Add models to the queue to generate deployment commands
+
+
+
+
+
@@ -705,7 +1129,7 @@
Extra Parame
-
+
+// Handle model search input
+function onModelSearchInput() {
+const query = modelSearch.value.toLowerCase().trim();
+
+if (query.length === 0) {
+ searchSuggestions.style.display = 'none';
+ clearModelSelection();
+ return;
+}
+
+// Fuzzy search
+const matches = Object.keys(supportedModels).filter(modelId =>
+ modelId.toLowerCase().includes(query)
+).slice(0, 10);
+
+if (matches.length > 0) {
+ showSearchSuggestions(matches);
+
+ // If exact match, select it
+ if (matches.includes(modelSearch.value)) {
+ onModelSearchChange();
+ }
+} else {
+ searchSuggestions.style.display = 'none';
+ clearModelSelection();
+}
+}
+
+// Show search suggestions
+function showSearchSuggestions(matches) {
+searchSuggestions.innerHTML = '';
+
+matches.forEach(modelId => {
+ const item = document.createElement('div');
+ item.className = 'suggestion-item';
+ item.textContent = modelId;
+ item.addEventListener('click', () => {
+ modelSearch.value = modelId;
+ searchSuggestions.style.display = 'none';
+ onModelSearchChange();
+ });
+ searchSuggestions.appendChild(item);
+});
+
+searchSuggestions.style.display = 'block';
+}
+
+// Handle model search focus
+function onModelSearchFocus() {
+if (modelSearch.value.trim()) {
+ onModelSearchInput();
+}
+}
+
+// Handle model search blur
+function onModelSearchBlur() {
+// Delay hiding to allow click on suggestions
+setTimeout(() => {
+ searchSuggestions.style.display = 'none';
+}, 200);
+}
+
+// Handle model search change
+function onModelSearchChange() {
+const modelId = modelSearch.value.trim();
+
+if (!modelId || !supportedModels[modelId]) {
+ clearModelSelection();
+ return;
+}
+
+updateModelConfiguration(modelId);
+}
+
+// Update model configuration (shared function)
+function updateModelConfiguration(modelId) {
+ if (!modelId || !supportedModels[modelId]) {
+ clearModelSelection();
+ return;
+ }
+
+ const model = supportedModels[modelId];
+ currentSelectedModel = modelId;
+
+ // Show model information
+ showModelInfo(model);
+
+ // Populate compatible options with auto-fill
+ populateInstanceSelect(model.supported_instances, true);
+ populateEngineSelect(model.supported_engines, true);
+ updateServiceSelect(model.supported_services, true);
+
+ // Update conditional parameter visibility and generate command
+ setTimeout(() => {
+ updateServiceSpecificParams();
+ updateEngineSpecificParams();
+ generateCommand();
+ }, 100); // Small delay to ensure selects are populated
+}
+
+// Show model information
+function showModelInfo(model) {
+document.getElementById('modelInfoTitle').textContent = `${model.model_id} Information`;
+document.getElementById('modelInfoDescription').textContent = model.description;
+
+// Create badges
+const badgesContainer = document.getElementById('modelInfoBadges');
+badgesContainer.innerHTML = '';
+
+const badges = [
+ { label: 'Type', value: model.model_type.toUpperCase() },
+ { label: 'Series', value: model.model_series?.name || 'N/A' },
+ { label: 'China Region', value: model.allow_china_region ? 'Yes' : 'No' }
+];
+
+if (model.application_scenario) {
+ badges.push({ label: 'Use Cases', value: model.application_scenario });
+}
+
+badges.forEach(badge => {
+ const span = document.createElement('span');
+ span.className = 'model-badge';
+ span.textContent = `${badge.label}: ${badge.value}`;
+ badgesContainer.appendChild(span);
+});
+
+// Add model links
+const linksContainer = document.getElementById('modelInfoLinks');
+linksContainer.innerHTML = '';
+
+if (model.huggingface_model_id) {
+ const hfLink = document.createElement('a');
+ hfLink.href = `https://huggingface.co/${model.huggingface_model_id}`;
+ hfLink.target = '_blank';
+ hfLink.textContent = '🤗 Hugging Face';
+ linksContainer.appendChild(hfLink);
+}
+
+if (model.modelscope_model_id) {
+ const msLink = document.createElement('a');
+ msLink.href = `https://modelscope.cn/models/${model.modelscope_model_id}`;
+ msLink.target = '_blank';
+ msLink.textContent = '🔬 ModelScope';
+ linksContainer.appendChild(msLink);
+}
+
+selectedModelInfo.style.display = 'block';
+}
+
+// Clear model selection
+function clearModelSelection() {
+ currentSelectedModel = null;
+ selectedModelInfo.style.display = 'none';
+ instanceSelect.innerHTML = '
';
+ engineSelect.innerHTML = '
';
+
+ // Clear table selection
+ document.querySelectorAll('.models-table tbody tr').forEach(row => {
+ row.classList.remove('selected');
+ });
+
+ // Clear command output
+ commandOutput.value = 'Select a model from the table to generate deployment command';
+ commandOutput.className = 'command-output empty';
+ copyButton.style.display = 'none';
+}
+
+// Populate instance select with real instance data
+function populateInstanceSelect(supportedInstanceTypes, autoFill = false) {
+instanceSelect.innerHTML = '
';
+
+supportedInstanceTypes.forEach((instanceType, index) => {
+ const instanceDef = window.EMD_HELPERS.getInstance(instanceType);
+ if (instanceDef) {
+ const option = document.createElement('option');
+ option.value = instanceType;
+
+ // Enhanced option text with real specifications
+ const specs = window.EMD_HELPERS.getInstanceSpecs(instanceType);
+ option.textContent = `${instanceType} (${specs.text})`;
+
+ // Auto-select first option if autoFill is true
+ if (autoFill && index === 0) {
+ option.selected = true;
+ }
+
+ instanceSelect.appendChild(option);
+ }
+});
+}
+
+// Populate engine select with real engine data
+function populateEngineSelect(supportedEngineTypes, autoFill = false) {
+engineSelect.innerHTML = '
';
+
+supportedEngineTypes.forEach((engineType, index) => {
+ const engineDef = window.EMD_HELPERS.getEngine(engineType);
+ if (engineDef) {
+ const option = document.createElement('option');
+ option.value = engineType;
+ option.textContent = `${engineType} - ${engineDef.description}`;
+
+ // Auto-select first option if autoFill is true
+ if (autoFill && index === 0) {
+ option.selected = true;
+ }
+
+ engineSelect.appendChild(option);
+ }
+});
+}
+
+// Update service select with real service data
+function updateServiceSelect(supportedServiceTypes, autoFill = false) {
+const options = serviceSelect.querySelectorAll('option');
+let firstAvailableOption = null;
+
+options.forEach(option => {
+ if (option.value === '') return;
+
+ if (supportedServiceTypes.includes(option.value)) {
+ option.disabled = false;
+ option.style.display = 'block';
+
+ // Update option text with real service info
+ const serviceDef = window.EMD_HELPERS.getService(option.value);
+ if (serviceDef) {
+ option.textContent = serviceDef.name;
+ }
+
+ // Track first available option for auto-fill
+ if (!firstAvailableOption) {
+ firstAvailableOption = option;
+ }
+ } else {
+ option.disabled = true;
+ option.style.display = 'none';
+ }
+});
+
+// Reset selection if current value is not supported
+if (serviceSelect.value && !supportedServiceTypes.includes(serviceSelect.value)) {
+ serviceSelect.value = '';
+}
+
+// Auto-select first available option if autoFill is true
+if (autoFill && firstAvailableOption && !serviceSelect.value) {
+ firstAvailableOption.selected = true;
+}
+}
+
+
+// Generate single command
+function generateCommand() {
+ const modelId = currentSelectedModel;
+ const instanceType = instanceSelect.value;
+ const engineType = engineSelect.value;
+ const serviceType = serviceSelect.value;
+
+ if (!modelId || !instanceType || !engineType || !serviceType) {
+ commandOutput.value = 'Please select all required parameters to generate deployment command';
+ commandOutput.className = 'command-output empty';
+ copyButton.style.display = 'none';
+ return;
+ }
+
+ let command = `emd deploy --model-id ${modelId} --instance-type ${instanceType} --engine-type ${engineType} --service-type ${serviceType}`;
+
+ // Add command-line arguments
+ const modelTag = document.getElementById('modelTag').value.trim();
+ if (modelTag) {
+ command += ` --model-tag ${modelTag}`;
+ }
+
+ // Add skip-confirm if checked
+ const skipConfirm = document.getElementById('skipConfirm').checked;
+ if (skipConfirm) {
+ command += ` --skip-confirm`;
+ }
+
+ // Build extra parameters
+ const extraParams = buildExtraParams();
+ if (Object.keys(extraParams).length > 0) {
+ command += ` --extra-params '${JSON.stringify(extraParams, null, 2)}'`;
+ }
+
+ // Check if run in background is enabled
+ const runInBackground = document.getElementById('runInBackground').checked;
+ if (runInBackground) {
+ command = `nohup ${command} > /dev/null 2>&1 &`;
+ }
+
+ // Update command output
+ commandOutput.value = command;
+ commandOutput.className = 'command-output';
+ copyButton.style.display = 'inline-block';
+}
+
+// Build extra parameters object
+function buildExtraParams() {
+ const extraParams = {};
+
+ // Model parameters
+ const modelParams = {};
+ const modelFilesS3Path = document.getElementById('modelFilesS3Path').value.trim();
+ const modelFilesDownloadSource = document.getElementById('modelFilesDownloadSource').value.trim();
+ const huggingfaceModelId = document.getElementById('huggingfaceModelId').value.trim();
+ const modelscopeModelId = document.getElementById('modelscopeModelId').value.trim();
+ const needPrepareModel = document.getElementById('needPrepareModel').checked;
+
+ if (modelFilesS3Path) modelParams.model_files_s3_path = modelFilesS3Path;
+ if (modelFilesDownloadSource) modelParams.model_files_download_source = modelFilesDownloadSource;
+ if (huggingfaceModelId) modelParams.huggingface_model_id = huggingfaceModelId;
+ if (modelscopeModelId) modelParams.modelscope_model_id = modelscopeModelId;
+ if (needPrepareModel) modelParams.need_prepare_model = false; // Checkbox is "skip preparation"
+
+ if (Object.keys(modelParams).length > 0) {
+ extraParams.model_params = modelParams;
+ }
+
+ // Service parameters
+ const serviceParams = {};
+ const apiKey = document.getElementById('apiKey').value.trim();
+
+ // SageMaker-specific parameters
+ const maxCapacity = document.getElementById('maxCapacity').value.trim();
+ const minCapacity = document.getElementById('minCapacity').value.trim();
+ const autoScalingTargetValue = document.getElementById('autoScalingTargetValue').value.trim();
+ const sagemakerEndpointName = document.getElementById('sagemakerEndpointName').value.trim();
+ const sagemakerVpcId = document.getElementById('sagemakerVpcId').value.trim();
+ const sagemakerSubnetIds = document.getElementById('sagemakerSubnetIds').value.trim();
+
+ // ECS-specific parameters
+ const desiredCapacity = document.getElementById('desiredCapacity').value.trim();
+ const maxSize = document.getElementById('maxSize').value.trim();
+ const vpcId = document.getElementById('vpcId').value.trim();
+ const subnetIds = document.getElementById('subnetIds').value.trim();
+ const useSpot = document.getElementById('useSpot').checked;
+
+ if (apiKey) serviceParams.api_key = apiKey;
+
+ // Add SageMaker params if service is SageMaker
+ const serviceType = serviceSelect.value;
+ if (serviceType === 'sagemaker_realtime' || serviceType === 'sagemaker_async') {
+ if (maxCapacity) serviceParams.max_capacity = parseInt(maxCapacity);
+ if (minCapacity) serviceParams.min_capacity = parseInt(minCapacity);
+ if (autoScalingTargetValue) serviceParams.auto_scaling_target_value = parseInt(autoScalingTargetValue);
+ if (sagemakerEndpointName) serviceParams.sagemaker_endpoint_name = sagemakerEndpointName;
+ if (sagemakerVpcId) serviceParams.vpc_id = sagemakerVpcId;
+ if (sagemakerSubnetIds) serviceParams.subnet_ids = sagemakerSubnetIds.split(',').map(s => s.trim());
+ }
+
+ // Add ECS params if service is ECS
+ if (serviceType === 'ecs') {
+ if (desiredCapacity) serviceParams.desired_capacity = parseInt(desiredCapacity);
+ if (maxSize) serviceParams.max_size = parseInt(maxSize);
+ if (vpcId) serviceParams.vpc_id = vpcId;
+ if (subnetIds) serviceParams.subnet_ids = subnetIds.split(',').map(s => s.trim());
+ if (useSpot) serviceParams.use_spot = true;
+ }
+
+ if (Object.keys(serviceParams).length > 0) {
+ extraParams.service_params = serviceParams;
+ }
+
+ // Engine parameters
+ const engineParams = {};
+ const envVars = document.getElementById('environmentVariables').value.trim();
+
+ // vLLM-specific parameters
+ const maxModelLen = document.getElementById('maxModelLen').value.trim();
+ const maxNumSeqs = document.getElementById('maxNumSeqs').value.trim();
+ const gpuMemoryUtilization = document.getElementById('gpuMemoryUtilization').value.trim();
+ const toolCallParser = document.getElementById('toolCallParser').value.trim();
+ const reasoningParser = document.getElementById('reasoningParser').value.trim();
+ const chatTemplate = document.getElementById('chatTemplate').value.trim();
+ const disableLogStats = document.getElementById('disableLogStats').checked;
+ const enableAutoToolChoice = document.getElementById('enableAutoToolChoice').checked;
+ const enableReasoning = document.getElementById('enableReasoning').checked;
+
+ // TGI-specific parameters
+ const maxTotalTokens = document.getElementById('maxTotalTokens').value.trim();
+ const maxConcurrentRequests = document.getElementById('maxConcurrentRequests').value.trim();
+ const maxBatchSize = document.getElementById('maxBatchSize').value.trim();
+ const maxInputTokens = document.getElementById('maxInputTokens').value.trim();
+
+ if (envVars) engineParams.environment_variables = envVars;
+
+ // Build default CLI args based on engine type
+ const engineType = engineSelect.value;
+ const defaultCliArgs = [];
+
+ if (engineType === 'vllm') {
+ if (maxModelLen) defaultCliArgs.push(`--max_model_len ${maxModelLen}`);
+ if (maxNumSeqs) defaultCliArgs.push(`--max_num_seqs ${maxNumSeqs}`);
+ if (gpuMemoryUtilization) defaultCliArgs.push(`--gpu_memory_utilization ${gpuMemoryUtilization}`);
+ if (toolCallParser) defaultCliArgs.push(`--tool_call_parser ${toolCallParser}`);
+ if (reasoningParser) defaultCliArgs.push(`--reasoning_parser ${reasoningParser}`);
+ if (chatTemplate) defaultCliArgs.push(`--chat_template ${chatTemplate}`);
+ if (disableLogStats) defaultCliArgs.push(`--disable_log_stats`);
+ if (enableAutoToolChoice) defaultCliArgs.push(`--enable_auto_tool_choice`);
+ if (enableReasoning) defaultCliArgs.push(`--enable_reasoning`);
+ } else if (engineType === 'tgi') {
+ if (maxTotalTokens) defaultCliArgs.push(`--max_total_tokens ${maxTotalTokens}`);
+ if (maxConcurrentRequests) defaultCliArgs.push(`--max_concurrent_requests ${maxConcurrentRequests}`);
+ if (maxBatchSize) defaultCliArgs.push(`--max_batch_size ${maxBatchSize}`);
+ if (maxInputTokens) defaultCliArgs.push(`--max_input_tokens ${maxInputTokens}`);
+ }
+
+ if (defaultCliArgs.length > 0) {
+ engineParams.default_cli_args = defaultCliArgs.join(' ');
+ }
+
+ if (Object.keys(engineParams).length > 0) {
+ extraParams.engine_params = engineParams;
+ }
+
+ // Framework parameters
+ const frameworkParams = {};
+ const limitConcurrency = document.getElementById('limitConcurrency').value.trim();
+ const timeoutKeepAlive = document.getElementById('timeoutKeepAlive').value.trim();
+ const uvicornLogLevel = document.getElementById('uvicornLogLevel').value.trim();
+
+ if (limitConcurrency) frameworkParams.limit_concurrency = parseInt(limitConcurrency);
+ if (timeoutKeepAlive) frameworkParams.timeout_keep_alive = parseInt(timeoutKeepAlive);
+ if (uvicornLogLevel) frameworkParams.uvicorn_log_level = uvicornLogLevel;
+
+ if (Object.keys(frameworkParams).length > 0) {
+ extraParams.framework_params = frameworkParams;
+ }
+
+ return extraParams;
+}
+
+// Copy commands to clipboard
+function copyCommands() {
+const commands = commandOutput.value;
+navigator.clipboard.writeText(commands).then(() => {
+ showCopySuccess();
+}).catch(err => {
+ console.error('Failed to copy commands: ', err);
+ // Fallback for older browsers
+ const textArea = document.createElement('textarea');
+ textArea.value = commands;
+ document.body.appendChild(textArea);
+ textArea.select();
+ document.execCommand('copy');
+ document.body.removeChild(textArea);
+ showCopySuccess();
+});
+}
+
+// Show copy success message
+function showCopySuccess() {
+copySuccess.classList.add('show');
+setTimeout(() => {
+ copySuccess.classList.remove('show');
+}, 3000);
+}
+
+// Reset form
+function resetForm() {
+ modelConfigForm.reset();
+ modelSearch.value = '';
+ clearModelSelection();
+ searchSuggestions.style.display = 'none';
+
+ // Reset service type to default
+ serviceSelect.value = 'sagemaker_realtime';
+
+ // Hide all conditional parameter sections
+ document.getElementById('sagemakerParams').style.display = 'none';
+ document.getElementById('ecsParams').style.display = 'none';
+ document.getElementById('vllmParams').style.display = 'none';
+ document.getElementById('tgiParams').style.display = 'none';
+}
+
+// Initialize when page loads
+document.addEventListener('DOMContentLoaded', function() {
+// Wait a bit for the config script to load
+setTimeout(initializeDynamicConfig, 100);
+});
+
+// Keyboard shortcuts
+document.addEventListener('keydown', function(e) {
+ // ESC to close panel
+ if (e.key === 'Escape' && isPanelOpen) {
+ closePanel();
+ }
+});
+