Download app.py from MrAlexGov/fastapi-vacancy-classifier: direct link, hf CLI and curl.
- Browser
- Download file 5.81 kB
-
https://huggingface.co/spaces/MrAlexGov/fastapi-vacancy-classifier/resolve/main/app.py
- Command line
-
hf download hf://spaces/MrAlexGov/fastapi-vacancy-classifier/app.py
-
curl -L -o app.py https://huggingface.co/spaces/MrAlexGov/fastapi-vacancy-classifier/resolve/main/app.py
5.81 kB
| from fastapi import FastAPI, HTTPException, Request | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pydantic import BaseModel | |
| from transformers import AutoTokenizer, AutoModelForSequenceClassification | |
| import torch | |
| import os | |
| import time | |
| # Инициализация приложения | |
| app = FastAPI( | |
| title="IT Job Classifier API", | |
| description="API для определения IT-вакансий по тексту", | |
| version="1.0.0", | |
| docs_url="/docs", # Явно включаем документацию | |
| redoc_url="/redoc", | |
| openapi_url="/openapi.json" | |
| ) | |
| # Добавляем CORS middleware | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Модели Pydantic | |
| class TextRequest(BaseModel): | |
| text: str | |
| class PredictionResponse(BaseModel): | |
| prediction: str | |
| is_it_job: bool | |
| confidence: float | |
| class_id: int | |
| # Глобальные переменные для модели | |
| MODEL_NAME = "MrAlexGov/BERT-AI-Vacancy" | |
| model = None | |
| tokenizer = None | |
| async def load_model(): | |
| """Загрузка модели при старте приложения""" | |
| global model, tokenizer | |
| print(f"🚀 Загружаем модель {MODEL_NAME}...") | |
| start_time = time.time() | |
| try: | |
| # Загружаем токенизатор и модель | |
| tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME) | |
| model = AutoModelForSequenceClassification.from_pretrained(MODEL_NAME) | |
| # Переводим модель в режим оценки | |
| model.eval() | |
| load_time = time.time() - start_time | |
| print(f"✅ Модель успешно загружена за {load_time:.2f} секунд") | |
| except Exception as e: | |
| print(f"❌ Ошибка загрузки модели: {str(e)}") | |
| raise e | |
| async def root(): | |
| """Корневой эндпоинт с документацией""" | |
| return { | |
| "service": "IT Job Classification API", | |
| "status": "running", | |
| "version": "1.0.0", | |
| "endpoints": { | |
| "GET /": "Эта документация", | |
| "GET /health": "Проверка здоровья сервиса", | |
| "POST /predict": "Классификация текста", | |
| "GET /docs": "Интерактивная документация (Swagger UI)", | |
| "GET /redoc": "Альтернативная документация" | |
| }, | |
| "model": { | |
| "name": MODEL_NAME, | |
| "status": "loaded" if model else "loading" | |
| }, | |
| "usage": { | |
| "curl_example": 'curl -X POST https://your-space.hf.space/predict -H "Content-Type: application/json" -d \'{"text": "Ищем Python разработчика"}\'' | |
| } | |
| } | |
| async def health(): | |
| """Проверка здоровья сервиса""" | |
| return { | |
| "status": "healthy", | |
| "timestamp": time.time(), | |
| "model_loaded": model is not None, | |
| "service": "IT Job Classifier" | |
| } | |
| async def predict(request: TextRequest): | |
| """Классифицирует текст на IT/не IT вакансию""" | |
| # Проверяем, загружена ли модель | |
| if model is None or tokenizer is None: | |
| raise HTTPException( | |
| status_code=503, | |
| detail="Модель еще не загружена. Пожалуйста, подождите." | |
| ) | |
| # Проверяем входные данные | |
| if not request.text or not request.text.strip(): | |
| raise HTTPException( | |
| status_code=400, | |
| detail="Текст не может быть пустым" | |
| ) | |
| try: | |
| # Токенизация | |
| inputs = tokenizer( | |
| request.text, | |
| return_tensors="pt", | |
| truncation=True, | |
| max_length=512, | |
| padding=True | |
| ) | |
| # Предсказание | |
| with torch.no_grad(): | |
| outputs = model(**inputs) | |
| probabilities = torch.nn.functional.softmax(outputs.logits, dim=-1) | |
| prediction_id = torch.argmax(probabilities, dim=-1).item() | |
| confidence = probabilities[0][prediction_id].item() | |
| # Интерпретация результата | |
| # Предполагаем, что label_1 - это IT-вакансия | |
| is_it_job = prediction_id == 1 | |
| prediction_label = "IT-вакансия" if is_it_job else "Не IT-вакансия" | |
| return PredictionResponse( | |
| prediction=prediction_label, | |
| is_it_job=is_it_job, | |
| confidence=confidence, | |
| class_id=prediction_id | |
| ) | |
| except Exception as e: | |
| raise HTTPException( | |
| status_code=500, | |
| detail=f"Ошибка обработки: {str(e)}" | |
| ) | |
| # Эндпоинт для тестирования | |
| async def test(): | |
| """Тестовый эндпоинт""" | |
| return { | |
| "message": "Сервис работает", | |
| "timestamp": time.time(), | |
| "model": MODEL_NAME | |
| } | |
| # Если запускаем напрямую (не через Docker на Spaces) | |
| if __name__ == "__main__": | |
| import uvicorn | |
| # Получаем порт из переменных окружения (для Hugging Face Spaces) | |
| port = int(os.getenv("PORT", 7860)) | |
| uvicorn.run( | |
| "app:app", | |
| host="0.0.0.0", | |
| port=port, | |
| reload=False # Отключаем reload в production | |
| ) |