Files
idea.llm.gitea.repo.docker.…/cli/detectors/python.js
2026-01-26 22:33:55 -06:00

187 lines
4.8 KiB
JavaScript

import { readFileSync, existsSync } from 'fs';
import { join } from 'path';
/**
* Detect Python project type and configuration
*/
export function detectPython(projectPath) {
const requirementsPath = join(projectPath, 'requirements.txt');
const pyprojectPath = join(projectPath, 'pyproject.toml');
const setupPath = join(projectPath, 'setup.py');
const hasRequirements = existsSync(requirementsPath);
const hasPyproject = existsSync(pyprojectPath);
const hasSetup = existsSync(setupPath);
if (!hasRequirements && !hasPyproject && !hasSetup) {
return null;
}
// Read requirements to detect project type
let requirements = '';
if (hasRequirements) {
requirements = readFileSync(requirementsPath, 'utf-8').toLowerCase();
}
// Read pyproject.toml for additional info
let pyproject = '';
if (hasPyproject) {
pyproject = readFileSync(pyprojectPath, 'utf-8').toLowerCase();
}
// Detect ML/PyTorch project
const mlIndicators = [
'torch',
'pytorch',
'tensorflow',
'keras',
'transformers',
'opencv',
'faster-whisper',
'whisper',
'scikit-learn',
'numpy'
];
const isML = mlIndicators.some(ind => requirements.includes(ind) || pyproject.includes(ind));
if (isML) {
return {
type: 'python-ml-pytorch',
dockerizable: true,
template: 'python/ml-pytorch',
port: detectPythonPort(projectPath) || 8000,
entryPoint: detectPythonEntryPoint(projectPath),
buildCommand: null,
description: 'Python ML/AI application',
systemDeps: detectSystemDeps(requirements)
};
}
// Check for web frameworks
const hasFlask = requirements.includes('flask') || pyproject.includes('flask');
const hasFastAPI = requirements.includes('fastapi') || pyproject.includes('fastapi');
const hasDjango = requirements.includes('django') || pyproject.includes('django');
let description = 'Python application';
let port = 8000;
if (hasFlask) {
description = 'Flask web application';
port = 5000;
} else if (hasFastAPI) {
description = 'FastAPI web application';
port = 8000;
} else if (hasDjango) {
description = 'Django web application';
port = 8000;
}
return {
type: 'python-standard',
dockerizable: true,
template: 'python/standard',
port: detectPythonPort(projectPath) || port,
entryPoint: detectPythonEntryPoint(projectPath),
buildCommand: null,
description,
framework: hasFlask ? 'flask' : hasFastAPI ? 'fastapi' : hasDjango ? 'django' : null
};
}
/**
* Detect Python entry point
*/
function detectPythonEntryPoint(projectPath) {
const commonEntries = [
'main.py',
'app.py',
'server.py',
'run.py',
'src/main.py',
'src/app.py'
];
for (const entry of commonEntries) {
if (existsSync(join(projectPath, entry))) {
return entry;
}
}
return 'main.py';
}
/**
* Detect port from Python files
*/
function detectPythonPort(projectPath) {
const mainFiles = ['main.py', 'app.py', 'server.py', 'run.py'];
for (const file of mainFiles) {
const filePath = join(projectPath, file);
if (existsSync(filePath)) {
const content = readFileSync(filePath, 'utf-8');
// Look for port definitions
const portMatch = content.match(/port[=\s:]+(\d{4})/i) ||
content.match(/PORT[=\s:]+(\d{4})/i);
if (portMatch) {
return parseInt(portMatch[1], 10);
}
}
}
return null;
}
/**
* Detect system dependencies needed for ML projects
*/
function detectSystemDeps(requirements) {
const deps = [];
if (requirements.includes('opencv') || requirements.includes('cv2')) {
deps.push('libgl1-mesa-glx', 'libglib2.0-0');
}
if (requirements.includes('whisper') || requirements.includes('faster-whisper')) {
deps.push('ffmpeg');
}
if (requirements.includes('soundfile') || requirements.includes('librosa')) {
deps.push('libsndfile1');
}
if (requirements.includes('pillow') || requirements.includes('pil')) {
deps.push('libjpeg-dev', 'zlib1g-dev');
}
return deps;
}
/**
* Get additional info about Python project
*/
export function getPythonInfo(projectPath) {
const requirementsPath = join(projectPath, 'requirements.txt');
const pyprojectPath = join(projectPath, 'pyproject.toml');
const info = {
hasRequirements: existsSync(requirementsPath),
hasPyproject: existsSync(pyprojectPath),
hasSetupPy: existsSync(join(projectPath, 'setup.py')),
hasVenv: existsSync(join(projectPath, 'venv')) || existsSync(join(projectPath, '.venv')),
dependencies: []
};
if (info.hasRequirements) {
const content = readFileSync(requirementsPath, 'utf-8');
info.dependencies = content
.split('\n')
.filter(line => line.trim() && !line.startsWith('#'))
.map(line => line.split('==')[0].split('>=')[0].split('<=')[0].trim());
}
return info;
}