Spaces:
Running
Running
Patryk Studzinski
commited on
Commit
·
9a9ec03
1
Parent(s):
b525236
adding-github-files-to-spaces
Browse files- .gitignore +52 -0
- Dockerfile +34 -0
- PROJECT_CONTEXT.md +107 -0
- README.md +292 -9
- answer.md +213 -0
- app/main.py +81 -0
- app/models/huggingface_service.py +111 -0
- app/schemas/schemas.py +12 -0
- download_model.py +69 -0
- requirements.txt +4 -0
- start_container.ps1 +23 -0
- start_container.sh +25 -0
.gitignore
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Byte-compiled / optimized / DLL files
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*.pyo
|
| 5 |
+
*.pyd
|
| 6 |
+
|
| 7 |
+
# Virtual environment
|
| 8 |
+
venv/
|
| 9 |
+
env/
|
| 10 |
+
|
| 11 |
+
# Model files and large data
|
| 12 |
+
/app/pretrain_model/
|
| 13 |
+
*.bin
|
| 14 |
+
*.safetensors
|
| 15 |
+
*.gguf
|
| 16 |
+
|
| 17 |
+
# Secrets
|
| 18 |
+
my_hf_token.txt
|
| 19 |
+
/run/secrets/
|
| 20 |
+
|
| 21 |
+
# Logs and debug files
|
| 22 |
+
*.log
|
| 23 |
+
*.out
|
| 24 |
+
*.err
|
| 25 |
+
|
| 26 |
+
# IDE and editor settings
|
| 27 |
+
.vscode/
|
| 28 |
+
.idea/
|
| 29 |
+
*.swp
|
| 30 |
+
*.swo
|
| 31 |
+
|
| 32 |
+
# Docker
|
| 33 |
+
*.env
|
| 34 |
+
*.dockerignore
|
| 35 |
+
docker-compose.override.yml
|
| 36 |
+
|
| 37 |
+
# Python package files
|
| 38 |
+
*.egg
|
| 39 |
+
*.egg-info/
|
| 40 |
+
dist/
|
| 41 |
+
build/
|
| 42 |
+
*.wheel
|
| 43 |
+
|
| 44 |
+
# Cache files
|
| 45 |
+
*.cache
|
| 46 |
+
*.mypy_cache/
|
| 47 |
+
*.pytest_cache/
|
| 48 |
+
*.ipynb_checkpoints/
|
| 49 |
+
|
| 50 |
+
# System files
|
| 51 |
+
.DS_Store
|
| 52 |
+
Thumbs.db
|
Dockerfile
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
FROM python:3.9-slim
|
| 3 |
+
|
| 4 |
+
# Set the working directory
|
| 5 |
+
WORKDIR /app
|
| 6 |
+
|
| 7 |
+
# Define where the model will be stored in the image
|
| 8 |
+
ENV MODEL_DIR=/app/pretrain_model
|
| 9 |
+
ENV HF_HUB_DISABLE_SYMLINKS_WARNING=1
|
| 10 |
+
|
| 11 |
+
# Copy the requirements file
|
| 12 |
+
COPY requirements.txt .
|
| 13 |
+
|
| 14 |
+
# Install dependencies
|
| 15 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 16 |
+
|
| 17 |
+
# Copy the download script into the image
|
| 18 |
+
COPY download_model.py /app/download_model.py
|
| 19 |
+
|
| 20 |
+
# Download the model using the script and the secret
|
| 21 |
+
RUN --mount=type=secret,id=huggingface_token \
|
| 22 |
+
echo "--- Docker RUN: Starting model download script /app/download_model.py..." && \
|
| 23 |
+
python /app/download_model.py && \
|
| 24 |
+
echo "--- Docker RUN: Model download script finished." && \
|
| 25 |
+
rm /app/download_model.py # Optional: clean up the script after use
|
| 26 |
+
|
| 27 |
+
# Copy the rest of your application code AFTER model download
|
| 28 |
+
COPY . .
|
| 29 |
+
|
| 30 |
+
# Expose the port the app runs on
|
| 31 |
+
EXPOSE 8000
|
| 32 |
+
|
| 33 |
+
# Run the application
|
| 34 |
+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
PROJECT_CONTEXT.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GPT4All Service - Project Context
|
| 2 |
+
|
| 3 |
+
## Project Overview
|
| 4 |
+
This is a **Polish Car Description Enhancement Service** built as a FastAPI microservice that uses a Hugging Face Large Language Model to generate enhanced marketing descriptions for cars in Polish language.
|
| 5 |
+
|
| 6 |
+
## Core Functionality
|
| 7 |
+
The service takes basic car information (make, model, year, mileage, features, condition) and generates compelling, marketing-friendly descriptions in Polish using the `speakleash/Bielik-1.5B-v3.0-Instruct` model - a Polish language model from the Bielik series.
|
| 8 |
+
|
| 9 |
+
## Project Structure
|
| 10 |
+
|
| 11 |
+
```
|
| 12 |
+
gpt4all-service/
|
| 13 |
+
├── app/
|
| 14 |
+
│ ├── main.py # FastAPI application with endpoints
|
| 15 |
+
│ ├── models/
|
| 16 |
+
│ │ └── huggingface_service.py # Core LLM service wrapper
|
| 17 |
+
│ └── schemas/
|
| 18 |
+
│ └── schemas.py # Pydantic data models
|
| 19 |
+
├── Dockerfile # Multi-stage Docker build
|
| 20 |
+
├── download_model.py # Model download script for Docker
|
| 21 |
+
├── requirements.txt # Python dependencies
|
| 22 |
+
├── start_container.ps1 # PowerShell startup script
|
| 23 |
+
├── start_container.sh # Bash startup script
|
| 24 |
+
└── README.md # Comprehensive documentation
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
## Technical Architecture
|
| 28 |
+
|
| 29 |
+
### 1. FastAPI Application (`app/main.py`)
|
| 30 |
+
- **Framework**: FastAPI with CORS middleware
|
| 31 |
+
- **Main Endpoint**: `POST /enhance-description` - takes car data, returns enhanced description
|
| 32 |
+
- **Health Check**: `GET /health` - service status and model initialization check
|
| 33 |
+
- **CORS**: Configured for frontend on `http://localhost:5173` (likely React/Vue dev server)
|
| 34 |
+
|
| 35 |
+
### 2. LLM Service (`app/models/huggingface_service.py`)
|
| 36 |
+
- **Purpose**: Wrapper around Hugging Face Transformers pipeline
|
| 37 |
+
- **Model**: `speakleash/Bielik-1.5B-v3.0-Instruct` (Polish language model)
|
| 38 |
+
- **Features**:
|
| 39 |
+
- Async initialization and text generation
|
| 40 |
+
- Support for both GPU (CUDA) and CPU inference
|
| 41 |
+
- Chat template support for conversation-style prompts
|
| 42 |
+
- Configurable generation parameters (temperature, top_p, max_tokens)
|
| 43 |
+
- Smart response parsing to extract only the assistant's response
|
| 44 |
+
|
| 45 |
+
### 3. Data Models (`app/schemas/schemas.py`)
|
| 46 |
+
- **CarData**: Input model with make, model, year, mileage, features[], condition
|
| 47 |
+
- **EnhancedDescriptionResponse**: Output model with generated description
|
| 48 |
+
|
| 49 |
+
### 4. Containerization
|
| 50 |
+
- **Docker**: Self-contained image with pre-downloaded model (~3.2GB)
|
| 51 |
+
- **Security**: Uses Docker BuildKit secrets for Hugging Face token handling
|
| 52 |
+
- **Model Storage**: Downloaded to `/app/pretrain_model` during build
|
| 53 |
+
- **Runtime**: Python 3.9-slim base image
|
| 54 |
+
|
| 55 |
+
## Key Technical Details
|
| 56 |
+
|
| 57 |
+
### Model Configuration
|
| 58 |
+
- **Model Path**: `/app/pretrain_model` (in container) or configurable for local dev
|
| 59 |
+
- **Device**: Currently set to CPU in main.py, but service supports GPU
|
| 60 |
+
- **Generation Params**: 150 max tokens, temperature 0.75, top_p 0.9
|
| 61 |
+
|
| 62 |
+
### Prompt Engineering
|
| 63 |
+
The service uses a carefully crafted Polish system prompt:
|
| 64 |
+
- Instructs the model to create marketing descriptions in Polish
|
| 65 |
+
- Limits output to 500 characters maximum
|
| 66 |
+
- Tells the model to ignore off-topic content
|
| 67 |
+
- Uses chat template format with system/user roles
|
| 68 |
+
|
| 69 |
+
### Dependencies
|
| 70 |
+
- **fastapi**: Web framework
|
| 71 |
+
- **uvicorn[standard]**: ASGI server
|
| 72 |
+
- **transformers[torch]**: Hugging Face transformers with PyTorch
|
| 73 |
+
- **accelerate**: Hugging Face optimization library
|
| 74 |
+
|
| 75 |
+
## Current State & Issues
|
| 76 |
+
|
| 77 |
+
### Git Status
|
| 78 |
+
- Modified `app/main.py` (likely recent changes)
|
| 79 |
+
- Deleted `app/models/gpt4all.py` (indicates migration from GPT4All to Hugging Face)
|
| 80 |
+
|
| 81 |
+
### Linter Issues in `huggingface_service.py`
|
| 82 |
+
1. Import issues: `pipeline` and `AutoTokenizer` imports need specific paths
|
| 83 |
+
2. Type annotations: `device: str = None` should be `Optional[str] = None`
|
| 84 |
+
3. Method parameters: Similar optional parameter typing issues
|
| 85 |
+
|
| 86 |
+
## Usage Scenarios
|
| 87 |
+
1. **Car Dealership Websites**: Auto-generate compelling descriptions from basic car specs
|
| 88 |
+
2. **Marketplace Applications**: Enhance user-provided car listings
|
| 89 |
+
3. **Inventory Management**: Bulk description generation for car databases
|
| 90 |
+
|
| 91 |
+
## Deployment Options
|
| 92 |
+
1. **Local Development**: Direct Python/uvicorn execution
|
| 93 |
+
2. **Docker Container**: Self-contained deployment with pre-downloaded model
|
| 94 |
+
3. **Production**: Containerized deployment with proper authentication
|
| 95 |
+
|
| 96 |
+
## Authentication Requirements
|
| 97 |
+
- Hugging Face Hub token required for model download (gated model)
|
| 98 |
+
- Token stored in `my_hf_token.txt` during Docker build
|
| 99 |
+
- Securely handled via Docker BuildKit secrets
|
| 100 |
+
|
| 101 |
+
## Performance Considerations
|
| 102 |
+
- Model size: ~3.2GB (significant memory footprint)
|
| 103 |
+
- CPU inference: Slower but more accessible
|
| 104 |
+
- GPU inference: Faster but requires CUDA setup
|
| 105 |
+
- Async design: Non-blocking text generation
|
| 106 |
+
|
| 107 |
+
This service represents a specialized AI application for the Polish automotive market, focusing on generating marketing content using state-of-the-art Polish language models.
|
README.md
CHANGED
|
@@ -1,12 +1,295 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
---
|
| 11 |
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## Contents
|
| 2 |
+
|
| 3 |
+
1. [Features](#features)
|
| 4 |
+
2. [Prerequisites](#prerequisites)
|
| 5 |
+
3. [Project Structure](#project-structure)
|
| 6 |
+
4. [Installation (Local Development)](#installation-local-development)
|
| 7 |
+
5. [Usage (Local Development)](#usage-local-development)
|
| 8 |
+
6. [Docker Usage](#docker-usage)
|
| 9 |
+
7. [Quick Start with PowerShell (`start_container.ps1`)](#quick-start-with-powershell-start_containerps1)
|
| 10 |
+
8. [API Endpoints](#api-endpoints)
|
| 11 |
+
- [Health Check](#health-check)
|
| 12 |
+
- [Enhance Description](#enhance-description)
|
| 13 |
+
9. [Core Service (`app/models/huggingface_service.py`)](#core-service-appmodelshuggingface_servicepy)
|
| 14 |
+
10. [Configuration](#configuration)
|
| 15 |
+
11. [Schemas (`app/schemas/schemas.py`)](#schemas-appschemasschemaspy)
|
| 16 |
+
- [CarData](#cardata)
|
| 17 |
+
- [EnhancedDescriptionResponse](#enhanceddescriptionresponse)
|
| 18 |
+
12. [Contributing](#contributing)
|
| 19 |
+
13. [License](#license)
|
| 20 |
+
|
| 21 |
---
|
| 22 |
+
|
| 23 |
+
# LLM Car Description Enhancer (Polish)
|
| 24 |
+
|
| 25 |
+
This repository contains a FastAPI application that utilizes a Hugging Face Transformers Large Language Model (specifically, `speakleash/Bielik-1.5B-v3.0-Instruct` or a similar model from the Bielik series) to generate enhanced marketing descriptions for cars, primarily in Polish.
|
| 26 |
+
|
| 27 |
+
The application is designed to be run locally for development or containerized using Docker for deployment. The LLM is baked into the Docker image for self-contained and efficient execution, which may require Hugging Face Hub authentication during the build process if the model is gated.
|
| 28 |
+
|
| 29 |
+
## Features
|
| 30 |
+
|
| 31 |
+
- Generate enhanced marketing descriptions for cars in Polish.
|
| 32 |
+
- Utilizes the `speakleash/Bielik-1.5B-v3.0-Instruct` model via the Hugging Face `transformers` library.
|
| 33 |
+
- Health check endpoint.
|
| 34 |
+
- Docker support for easy deployment, with the model included in the image.
|
| 35 |
+
- Includes a `start_container.sh` script for convenient container startup.
|
| 36 |
+
|
| 37 |
+
## Prerequisites
|
| 38 |
+
|
| 39 |
+
- Python 3.9 or higher
|
| 40 |
+
- `pip` (Python package installer)
|
| 41 |
+
- Docker (for containerized deployment, Docker BuildKit enabled recommended for secrets)
|
| 42 |
+
- Git (for cloning the repository)
|
| 43 |
+
- A Hugging Face Hub account and an access token (with `read` permissions) if the chosen model is gated (see Docker Usage section).
|
| 44 |
+
- For using `start_container.sh`: A bash-compatible shell (like those on Linux, macOS, or Git Bash on Windows).
|
| 45 |
+
|
| 46 |
+
## Project Structure
|
| 47 |
+
|
| 48 |
+
A typical layout for this project would be:
|
| 49 |
+
|
| 50 |
+
```text
|
| 51 |
+
.
|
| 52 |
+
├── app/
|
| 53 |
+
│ ├── __init__.py
|
| 54 |
+
│ ├── main.py # FastAPI application, endpoints
|
| 55 |
+
│ ├── models/
|
| 56 |
+
│ │ ├── __init__.py
|
| 57 |
+
│ │ └── huggingface_service.py # Service for interacting with the LLM
|
| 58 |
+
│ └── schemas/
|
| 59 |
+
│ ├── __init__.py
|
| 60 |
+
│ └── schemas.py # Pydantic schemas for request/response
|
| 61 |
+
├── .gitignore
|
| 62 |
+
├── Dockerfile
|
| 63 |
+
├── download_model.py # Script to download model during Docker build
|
| 64 |
+
├── my_hf_token.txt # (Should be created locally) For storing HF token
|
| 65 |
+
├── requirements.txt
|
| 66 |
+
├── start_container.sh # Helper script to run the Docker container
|
| 67 |
+
└── README.md
|
| 68 |
+
|
| 69 |
+
```
|
| 70 |
+
|
| 71 |
+
## Installation (Local Development)
|
| 72 |
+
|
| 73 |
+
1. **Clone the repository:**
|
| 74 |
+
```bash
|
| 75 |
+
git clone [https://github.com/studzin-sky/llm-description-enhancer.git](https://github.com/studzin-sky/llm-description-enhancer.git)
|
| 76 |
+
cd llm-description-enhancer
|
| 77 |
+
```
|
| 78 |
+
|
| 79 |
+
2. **Create and activate a virtual environment:**
|
| 80 |
+
(Recommended to keep dependencies isolated)
|
| 81 |
+
```bash
|
| 82 |
+
python -m venv venv
|
| 83 |
+
```
|
| 84 |
+
* On macOS/Linux:
|
| 85 |
+
```bash
|
| 86 |
+
source venv/bin/activate
|
| 87 |
+
```
|
| 88 |
+
* On Windows (PowerShell):
|
| 89 |
+
```bash
|
| 90 |
+
.\venv\Scripts\Activate.ps1
|
| 91 |
+
```
|
| 92 |
+
* On Windows (Command Prompt):
|
| 93 |
+
```bash
|
| 94 |
+
venv\Scripts\activate.bat
|
| 95 |
+
```
|
| 96 |
+
|
| 97 |
+
3. **Install the required dependencies:**
|
| 98 |
+
Ensure your `requirements.txt` includes `fastapi`, `uvicorn[standard]`, `transformers[torch]`, `torch`, `accelerate`, and `huggingface_hub`.
|
| 99 |
+
```bash
|
| 100 |
+
pip install -r requirements.txt
|
| 101 |
+
```
|
| 102 |
+
*Note: The first time you run the application locally (or if the model cache is empty), the Hugging Face model (~3.2GB) will be downloaded. This might take some time. **If the model (`speakleash/Bielik-1.5B-v3.0-Instruct` or the one configured) is gated or requires authentication, you may need to log in using `huggingface-cli login` in your terminal before running the application locally.** After logging in, your token will be cached by the `huggingface_hub` library.*
|
| 103 |
+
|
| 104 |
+
## Usage (Local Development)
|
| 105 |
+
|
| 106 |
+
1. **Start the FastAPI server:**
|
| 107 |
+
From the project root directory:
|
| 108 |
+
```bash
|
| 109 |
+
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
|
| 110 |
+
```
|
| 111 |
+
* `--reload` enables auto-reloading for development.
|
| 112 |
+
* `--host 0.0.0.0` makes the server accessible on your network.
|
| 113 |
+
|
| 114 |
+
2. **Access the application:**
|
| 115 |
+
* Health Check: [http://127.0.0.1:8000/health](http://127.0.0.1:8000/health)
|
| 116 |
+
* API Documentation (Swagger UI): [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)
|
| 117 |
+
* Enhance Description: `POST` requests to [http://127.0.0.1:8000/enhance-description](http://127.0.0.1:8000/enhance-description)
|
| 118 |
+
|
| 119 |
+
## Docker Usage
|
| 120 |
+
|
| 121 |
+
The included `Dockerfile` builds an image with the application and the pre-downloaded Hugging Face model, making it self-contained. Downloading gated models during the build process requires a Hugging Face Hub token.
|
| 122 |
+
|
| 123 |
+
1. **Prepare Hugging Face Hub Token (for Gated Models):**
|
| 124 |
+
The `speakleash/Bielik-1.5B-v3.0-Instruct` model may require authentication to download.
|
| 125 |
+
* **Get a Token:**
|
| 126 |
+
1. Go to your Hugging Face account settings: [https://huggingface.co/settings/tokens](https://huggingface.co/settings/tokens)
|
| 127 |
+
2. Create a new token (e.g., named "docker-bielik-access") with `read` permissions.
|
| 128 |
+
3. Copy the generated token (it will start with `hf_`).
|
| 129 |
+
* **Create Token File:**
|
| 130 |
+
1. In your project's root directory (next to your `Dockerfile`), create a file named `my_hf_token.txt`.
|
| 131 |
+
2. Paste **only the token string** (e.g., `hf_YourActualTokenValueHere`) into this file. Do not add any other text or variable names.
|
| 132 |
+
|
| 133 |
+
2. **Build the Docker image:**
|
| 134 |
+
From the project root directory, run:
|
| 135 |
+
```bash
|
| 136 |
+
DOCKER_BUILDKIT=1 docker build --secret id=huggingface_token,src=my_hf_token.txt -t llm-description-enhancer .
|
| 137 |
+
```
|
| 138 |
+
* `DOCKER_BUILDKIT=1`: Enables BuildKit, which is required for using `--secret`.
|
| 139 |
+
* `--secret id=huggingface_token,src=my_hf_token.txt`: Securely provides the content of `my_hf_token.txt` to the build process. The `id=huggingface_token` must match the ID used in the `RUN --mount` directive in your `Dockerfile`.
|
| 140 |
+
* *(This step will take a while, especially the first time, as it downloads the LLM using your token).*
|
| 141 |
+
|
| 142 |
+
3. **Run the Docker container using the Helper Script (`start_container.sh`):**
|
| 143 |
+
A helper script `start_container.sh` is included in the repository to simplify starting the Docker container. This script typically handles stopping/removing any pre-existing container with the same configured name and then starts a new one.
|
| 144 |
+
|
| 145 |
+
* **Ensure the script is executable:**
|
| 146 |
+
After cloning the repository, or if the execute permission isn't set, you might need to make the script executable (on Linux, macOS, or Git Bash on Windows):
|
| 147 |
+
```bash
|
| 148 |
+
chmod +x start_container.sh
|
| 149 |
+
```
|
| 150 |
+
|
| 151 |
+
* **Run the script:**
|
| 152 |
+
From the project root directory:
|
| 153 |
+
```bash
|
| 154 |
+
./start_container.sh
|
| 155 |
+
```
|
| 156 |
+
|
| 157 |
+
* **Expected Outcome (depends on your script's content):**
|
| 158 |
+
The script will likely:
|
| 159 |
+
* Output messages indicating it's managing the container.
|
| 160 |
+
* Start the container (possibly in detached mode).
|
| 161 |
+
* Inform you that the service is available at `http://127.0.0.1:8000`.
|
| 162 |
+
* Provide commands to view logs or stop the container if it's running in detached mode (e.g., `docker logs <container_name> -f` and `docker stop <container_name>`).
|
| 163 |
+
|
| 164 |
+
*(Alternatively, you can run the container manually: `docker run --rm -p 8000:8000 llm-description-enhancer`)*
|
| 165 |
+
|
| 166 |
+
4. **Test the containerized application:**
|
| 167 |
+
Once the container is running (via the script or manually), send requests to `http://127.0.0.1:8000` as described in the API Endpoints section.
|
| 168 |
+
|
| 169 |
+
## Quick Start with PowerShell (`start_container.ps1`)
|
| 170 |
+
|
| 171 |
+
For Windows users, you can automate the Docker build and run process using the provided PowerShell script. This script will:
|
| 172 |
+
- Build the Docker image using your Hugging Face token (from `my_hf_token.txt`)
|
| 173 |
+
- Stop and remove any existing container named `bielik_app_instance`
|
| 174 |
+
- Start a new container and map port 8000
|
| 175 |
+
|
| 176 |
+
**Steps:**
|
| 177 |
+
|
| 178 |
+
1. Ensure your Hugging Face token is saved in `my_hf_token.txt` in the project root (see above for details).
|
| 179 |
+
2. Open PowerShell in the project directory.
|
| 180 |
+
3. (Optional, but recommended) Temporarily allow running unsigned scripts for this session:
|
| 181 |
+
```powershell
|
| 182 |
+
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process
|
| 183 |
+
```
|
| 184 |
+
4. Run the script:
|
| 185 |
+
```powershell
|
| 186 |
+
.\start_container.ps1
|
| 187 |
+
```
|
| 188 |
+
|
| 189 |
+
The script will build the image and start the container. Your FastAPI service will be available at [http://127.0.0.1:8000](http://127.0.0.1:8000).
|
| 190 |
+
|
| 191 |
+
You can view logs with:
|
| 192 |
+
```powershell
|
| 193 |
+
docker logs bielik_app_instance -f
|
| 194 |
+
```
|
| 195 |
+
To stop the container:
|
| 196 |
+
```powershell
|
| 197 |
+
docker stop bielik_app_instance
|
| 198 |
+
```
|
| 199 |
+
|
| 200 |
+
If you encounter a security error about script signing, see the [Microsoft documentation on execution policies](https://go.microsoft.com/fwlink/?LinkID=135170).
|
| 201 |
+
|
| 202 |
---
|
| 203 |
|
| 204 |
+
## API Endpoints
|
| 205 |
+
|
| 206 |
+
### Health Check
|
| 207 |
+
|
| 208 |
+
- **Endpoint:** `/health`
|
| 209 |
+
- **Method:** `GET`
|
| 210 |
+
- **Description:** Returns the status of the application and model initialization.
|
| 211 |
+
- **Example Response:**
|
| 212 |
+
```json
|
| 213 |
+
{
|
| 214 |
+
"status": "ok",
|
| 215 |
+
"model_initialized": true,
|
| 216 |
+
"model_path": "/app/pretrain_model"
|
| 217 |
+
}
|
| 218 |
+
```
|
| 219 |
+
|
| 220 |
+
### Enhance Description
|
| 221 |
+
|
| 222 |
+
- **Endpoint:** `/enhance-description`
|
| 223 |
+
- **Method:** `POST`
|
| 224 |
+
- **Description:** Generates an enhanced marketing description for a car in Polish.
|
| 225 |
+
- **Request Body (`application/json`):**
|
| 226 |
+
```json
|
| 227 |
+
{
|
| 228 |
+
"make": "Volkswagen",
|
| 229 |
+
"model": "Golf",
|
| 230 |
+
"year": 2022,
|
| 231 |
+
"mileage": 15000,
|
| 232 |
+
"features": ["Klimatyzacja automatyczna", "System nawigacji", "Czujniki parkowania"],
|
| 233 |
+
"condition": "Bardzo dobry"
|
| 234 |
+
}
|
| 235 |
+
```
|
| 236 |
+
- **Response (`application/json`):**
|
| 237 |
+
```json
|
| 238 |
+
{
|
| 239 |
+
"description": "Wygenerowany przez AI opis samochodu..."
|
| 240 |
+
}
|
| 241 |
+
```
|
| 242 |
+
- **Example cURL request (for Git Bash / bash-like shells):**
|
| 243 |
+
```bash
|
| 244 |
+
curl -X POST "http://127.0.0.1:8000/enhance-description" \
|
| 245 |
+
-H "Content-Type: application/json" \
|
| 246 |
+
-d '{
|
| 247 |
+
"make": "Toyota",
|
| 248 |
+
"model": "Corolla",
|
| 249 |
+
"year": 2021,
|
| 250 |
+
"mileage": 25000,
|
| 251 |
+
"features": ["Kamera cofania", "Apple CarPlay", "Android Auto", "System bezkluczykowy"],
|
| 252 |
+
"condition": "Bardzo dobry"
|
| 253 |
+
}'
|
| 254 |
+
```
|
| 255 |
+
|
| 256 |
+
## Core Service (`app/models/huggingface_service.py`)
|
| 257 |
+
|
| 258 |
+
The `HuggingFaceTextGenerationService` class handles the interaction with the Large Language Model.
|
| 259 |
+
|
| 260 |
+
- **Key Methods:**
|
| 261 |
+
- `async initialize()`: Loads the pre-trained model and tokenizer from the path specified during service instantiation (e.g., `/app/pretrain_model` in Docker, or from Hugging Face cache locally).
|
| 262 |
+
- `async generate_text(chat_template_messages: list, max_new_tokens: int, ...)`: Generates text based on a structured chat prompt, applying appropriate chat templates and parsing the model's output to return only the assistant's response.
|
| 263 |
+
|
| 264 |
+
## Configuration
|
| 265 |
+
|
| 266 |
+
- **Model Used:** `speakleash/Bielik-1.5B-v3.0-Instruct`. This is baked into `/app/pretrain_model` in the Docker image. For local development, it's downloaded to the Hugging Face cache.
|
| 267 |
+
- **Language:** The primary focus is on generating descriptions in **Polish**.
|
| 268 |
+
- **Prompt Engineering:** The system and user prompts in `app/main.py` are crafted to guide the model towards generating concise and relevant marketing descriptions.
|
| 269 |
+
|
| 270 |
+
## Schemas (`app/schemas/schemas.py`)
|
| 271 |
+
|
| 272 |
+
Pydantic models are used for request and response validation.
|
| 273 |
+
|
| 274 |
+
### `CarData`
|
| 275 |
+
|
| 276 |
+
- **Fields:**
|
| 277 |
+
- `make`: `str`
|
| 278 |
+
- `model`: `str`
|
| 279 |
+
- `year`: `int`
|
| 280 |
+
- `mileage`: `int`
|
| 281 |
+
- `features`: `list[str]`
|
| 282 |
+
- `condition`: `str`
|
| 283 |
+
|
| 284 |
+
### `EnhancedDescriptionResponse`
|
| 285 |
+
|
| 286 |
+
- **Fields:**
|
| 287 |
+
- `description`: `str`
|
| 288 |
+
|
| 289 |
+
## Contributing
|
| 290 |
+
|
| 291 |
+
Contributions are welcome! Please open an issue or submit a pull request for any changes.
|
| 292 |
+
|
| 293 |
+
## License
|
| 294 |
+
|
| 295 |
+
This project is licensed under the MIT License.
|
answer.md
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Great choice! Creating a separate service for GPT4All is a clean and scalable approach. Let’s break this down step by step, starting from setting up the project structure to deploying the service.
|
| 2 |
+
|
| 3 |
+
---
|
| 4 |
+
|
| 5 |
+
### **1. Project Structure**
|
| 6 |
+
Here’s a suggested folder structure for your GPT4All service:
|
| 7 |
+
|
| 8 |
+
```
|
| 9 |
+
gpt4all-service/
|
| 10 |
+
├── app/
|
| 11 |
+
│ ├── __init__.py
|
| 12 |
+
│ ├── main.py # FastAPI/Flask app entry point
|
| 13 |
+
│ ├── models/ # GPT4All model loading and inference logic
|
| 14 |
+
│ │ └── gpt4all.py
|
| 15 |
+
│ └── schemas/ # Pydantic models for request/response validation
|
| 16 |
+
│ └── schemas.py
|
| 17 |
+
├── tests/ # Unit and integration tests
|
| 18 |
+
│ └── test_api.py
|
| 19 |
+
├── requirements.txt # Python dependencies
|
| 20 |
+
├── Dockerfile # For containerization
|
| 21 |
+
├── README.md # Project documentation
|
| 22 |
+
└── .env # Environment variables (optional)
|
| 23 |
+
```
|
| 24 |
+
|
| 25 |
+
---
|
| 26 |
+
|
| 27 |
+
### **2. Setting Up the Project**
|
| 28 |
+
1. **Create the Project Folder**:
|
| 29 |
+
```bash
|
| 30 |
+
mkdir gpt4all-service
|
| 31 |
+
cd gpt4all-service
|
| 32 |
+
```
|
| 33 |
+
|
| 34 |
+
2. **Initialize a Virtual Environment**:
|
| 35 |
+
```bash
|
| 36 |
+
python -m venv venv
|
| 37 |
+
source venv/bin/activate # On Windows: venv\Scripts\activate
|
| 38 |
+
```
|
| 39 |
+
|
| 40 |
+
3. **Install Dependencies**:
|
| 41 |
+
Create a `requirements.txt` file:
|
| 42 |
+
```plaintext
|
| 43 |
+
fastapi
|
| 44 |
+
uvicorn
|
| 45 |
+
gpt4all
|
| 46 |
+
pydantic
|
| 47 |
+
python-dotenv
|
| 48 |
+
```
|
| 49 |
+
|
| 50 |
+
Install the dependencies:
|
| 51 |
+
```bash
|
| 52 |
+
pip install -r requirements.txt
|
| 53 |
+
```
|
| 54 |
+
|
| 55 |
+
---
|
| 56 |
+
|
| 57 |
+
### **3. Build the GPT4All Service**
|
| 58 |
+
#### **Step 1: Create the Model Loading Logic**
|
| 59 |
+
- Create `app/models/gpt4all.py`:
|
| 60 |
+
```python
|
| 61 |
+
from gpt4all import GPT4All
|
| 62 |
+
|
| 63 |
+
class GPT4AllService:
|
| 64 |
+
def __init__(self, model_path: str):
|
| 65 |
+
self.model = GPT4All(model_path)
|
| 66 |
+
|
| 67 |
+
def generate_description(self, prompt: str) -> str:
|
| 68 |
+
response = self.model.generate(prompt, max_tokens=300)
|
| 69 |
+
return response
|
| 70 |
+
```
|
| 71 |
+
|
| 72 |
+
#### **Step 2: Define Request/Response Schemas**
|
| 73 |
+
- Create `app/schemas/schemas.py`:
|
| 74 |
+
```python
|
| 75 |
+
from pydantic import BaseModel
|
| 76 |
+
|
| 77 |
+
class CarData(BaseModel):
|
| 78 |
+
make: str
|
| 79 |
+
model: str
|
| 80 |
+
year: int
|
| 81 |
+
mileage: int
|
| 82 |
+
features: list[str]
|
| 83 |
+
condition: str
|
| 84 |
+
|
| 85 |
+
class EnhancedDescriptionResponse(BaseModel):
|
| 86 |
+
description: str
|
| 87 |
+
```
|
| 88 |
+
|
| 89 |
+
#### **Step 3: Create the FastAPI App**
|
| 90 |
+
- Create `app/main.py`:
|
| 91 |
+
```python
|
| 92 |
+
from fastapi import FastAPI, HTTPException
|
| 93 |
+
from app.models.gpt4all import GPT4AllService
|
| 94 |
+
from app.schemas.schemas import CarData, EnhancedDescriptionResponse
|
| 95 |
+
|
| 96 |
+
app = FastAPI()
|
| 97 |
+
|
| 98 |
+
# Initialize GPT4All service
|
| 99 |
+
gpt4all_service = GPT4AllService("ggml-model-gpt4all-falcon-q4_0.bin")
|
| 100 |
+
|
| 101 |
+
@app.post("/enhance-description", response_model=EnhancedDescriptionResponse)
|
| 102 |
+
async def enhance_description(car_data: CarData):
|
| 103 |
+
try:
|
| 104 |
+
# Create a prompt from car data
|
| 105 |
+
prompt = f"""
|
| 106 |
+
Enhance this car description for an auction portal:
|
| 107 |
+
- Make: {car_data.make}
|
| 108 |
+
- Model: {car_data.model}
|
| 109 |
+
- Year: {car_data.year}
|
| 110 |
+
- Mileage: {car_data.mileage}
|
| 111 |
+
- Features: {', '.join(car_data.features)}
|
| 112 |
+
- Condition: {car_data.condition}
|
| 113 |
+
"""
|
| 114 |
+
# Generate description
|
| 115 |
+
description = gpt4all_service.generate_description(prompt)
|
| 116 |
+
return {"description": description}
|
| 117 |
+
except Exception as e:
|
| 118 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 119 |
+
```
|
| 120 |
+
|
| 121 |
+
---
|
| 122 |
+
|
| 123 |
+
### **4. Run the Service**
|
| 124 |
+
1. **Start the Service**:
|
| 125 |
+
```bash
|
| 126 |
+
uvicorn app.main:app --reload --port 8000
|
| 127 |
+
```
|
| 128 |
+
|
| 129 |
+
2. **Test the API**:
|
| 130 |
+
Use `curl` or Postman to send a POST request:
|
| 131 |
+
```bash
|
| 132 |
+
curl -X POST "http://localhost:8000/enhance-description" \
|
| 133 |
+
-H "Content-Type: application/json" \
|
| 134 |
+
-d '{
|
| 135 |
+
"make": "Toyota",
|
| 136 |
+
"model": "Camry",
|
| 137 |
+
"year": 2020,
|
| 138 |
+
"mileage": 45000,
|
| 139 |
+
"features": ["sunroof", "leather seats", "lane assist"],
|
| 140 |
+
"condition": "excellent"
|
| 141 |
+
}'
|
| 142 |
+
```
|
| 143 |
+
|
| 144 |
+
**Expected Response**:
|
| 145 |
+
```json
|
| 146 |
+
{
|
| 147 |
+
"description": "This 2020 Toyota Camry is a well-maintained vehicle with only 45,000 miles on the odometer. It comes equipped with a sunroof, luxurious leather seats, and advanced lane assist technology. Perfect for families or commuters, this car is in excellent condition and ready to hit the road. Don’t miss out on this fantastic deal!"
|
| 148 |
+
}
|
| 149 |
+
```
|
| 150 |
+
|
| 151 |
+
---
|
| 152 |
+
|
| 153 |
+
### **5. Add Logging and Error Handling**
|
| 154 |
+
- Add logging to track requests and errors:
|
| 155 |
+
```python
|
| 156 |
+
import logging
|
| 157 |
+
|
| 158 |
+
logging.basicConfig(level=logging.INFO)
|
| 159 |
+
logger = logging.getLogger(__name__)
|
| 160 |
+
|
| 161 |
+
@app.post("/enhance-description", response_model=EnhancedDescriptionResponse)
|
| 162 |
+
async def enhance_description(car_data: CarData):
|
| 163 |
+
try:
|
| 164 |
+
logger.info(f"Received request for car: {car_data.make} {car_data.model}")
|
| 165 |
+
prompt = f"Enhance this car description: {car_data}"
|
| 166 |
+
description = gpt4all_service.generate_description(prompt)
|
| 167 |
+
return {"description": description}
|
| 168 |
+
except Exception as e:
|
| 169 |
+
logger.error(f"Error generating description: {e}")
|
| 170 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 171 |
+
```
|
| 172 |
+
|
| 173 |
+
---
|
| 174 |
+
|
| 175 |
+
### **6. Containerize the Service (Optional)**
|
| 176 |
+
Create a `Dockerfile` to containerize the service:
|
| 177 |
+
```dockerfile
|
| 178 |
+
# Use an official Python runtime as a parent image
|
| 179 |
+
FROM python:3.9-slim
|
| 180 |
+
|
| 181 |
+
# Set the working directory
|
| 182 |
+
WORKDIR /app
|
| 183 |
+
|
| 184 |
+
# Copy the requirements file
|
| 185 |
+
COPY requirements.txt .
|
| 186 |
+
|
| 187 |
+
# Install dependencies
|
| 188 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 189 |
+
|
| 190 |
+
# Copy the application code
|
| 191 |
+
COPY . .
|
| 192 |
+
|
| 193 |
+
# Expose the port the app runs on
|
| 194 |
+
EXPOSE 8000
|
| 195 |
+
|
| 196 |
+
# Run the application
|
| 197 |
+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
| 198 |
+
```
|
| 199 |
+
|
| 200 |
+
Build and run the Docker container:
|
| 201 |
+
```bash
|
| 202 |
+
docker build -t gpt4all-service .
|
| 203 |
+
docker run -p 8000:8000 gpt4all-service
|
| 204 |
+
```
|
| 205 |
+
|
| 206 |
+
---
|
| 207 |
+
|
| 208 |
+
### **7. Next Steps**
|
| 209 |
+
- Add unit tests in the `tests/` folder.
|
| 210 |
+
- Add environment variables for configuration (e.g., model path, port).
|
| 211 |
+
- Integrate with your Flask backend by calling this service via HTTP.
|
| 212 |
+
|
| 213 |
+
Let me know if you need help with any specific part (e.g., testing, deployment, or advanced features)!
|
app/main.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI, HTTPException
|
| 2 |
+
from app.models.huggingface_service import HuggingFaceTextGenerationService
|
| 3 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 4 |
+
from app.schemas.schemas import CarData, EnhancedDescriptionResponse
|
| 5 |
+
|
| 6 |
+
app = FastAPI()
|
| 7 |
+
|
| 8 |
+
app.add_middleware(
|
| 9 |
+
CORSMiddleware,
|
| 10 |
+
allow_origins=["http://localhost:5173"],
|
| 11 |
+
allow_credentials=True,
|
| 12 |
+
allow_methods=["*"],
|
| 13 |
+
allow_headers=["*"],
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
MODEL_PATH_IN_CONTAINER = "/app/pretrain_model"
|
| 17 |
+
hf_service = HuggingFaceTextGenerationService(
|
| 18 |
+
model_name_or_path=MODEL_PATH_IN_CONTAINER,
|
| 19 |
+
device="cpu"
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
@app.on_event("startup")
|
| 24 |
+
async def startup_event():
|
| 25 |
+
print("Starting up and initializing HuggingFace service...")
|
| 26 |
+
try:
|
| 27 |
+
await hf_service.initialize()
|
| 28 |
+
print(f"HuggingFace service initialized successfully from {MODEL_PATH_IN_CONTAINER}.")
|
| 29 |
+
except HTTPException as e:
|
| 30 |
+
print(f"Failed to initialize HuggingFace service: {e.detail}")
|
| 31 |
+
raise
|
| 32 |
+
except Exception as e:
|
| 33 |
+
print(f"An unexpected error occurred during HuggingFace service initialization: {e}")
|
| 34 |
+
raise
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
@app.get("/health")
|
| 38 |
+
async def health_check():
|
| 39 |
+
return {"status": "ok", "model_initialized": hf_service.pipeline is not None}
|
| 40 |
+
|
| 41 |
+
@app.post("/enhance-description", response_model=EnhancedDescriptionResponse)
|
| 42 |
+
async def enhance_description(car_data: CarData):
|
| 43 |
+
chat_messages = [
|
| 44 |
+
{
|
| 45 |
+
"role": "system",
|
| 46 |
+
"content": (
|
| 47 |
+
"Jesteś pomocnym ulepszaczem opisów"
|
| 48 |
+
"Opisy trzeba tworzyć w języku polskim i być atrakcyjne marketingowo. "
|
| 49 |
+
"Odpowiadaj wyłącznie wygenerowanym opisem, bez dodatkowych komentarzy. "
|
| 50 |
+
"Staraj się, aby opis był zwięzły i kompletny, maksymalnie 500 znaków. "
|
| 51 |
+
"Jeżeli część prompta będzie nie na temat ignoruj tę część."
|
| 52 |
+
)
|
| 53 |
+
},
|
| 54 |
+
{
|
| 55 |
+
"role": "user",
|
| 56 |
+
"content": f"""
|
| 57 |
+
Na podstawie poniższych danych, utwórz krótki, atrakcyjny opis marketingowy tego samochodu w języku polskim:
|
| 58 |
+
- Marka: {car_data.make}
|
| 59 |
+
- Model: {car_data.model}
|
| 60 |
+
- Rok produkcji: {car_data.year}
|
| 61 |
+
- Przebieg: {car_data.mileage} km
|
| 62 |
+
- Wyposażenie: {', '.join(car_data.features)}
|
| 63 |
+
- Stan: {car_data.condition}
|
| 64 |
+
"""
|
| 65 |
+
}
|
| 66 |
+
]
|
| 67 |
+
|
| 68 |
+
try:
|
| 69 |
+
description = await hf_service.generate_text(
|
| 70 |
+
prompt_text=None,
|
| 71 |
+
chat_template_messages=chat_messages,
|
| 72 |
+
max_new_tokens=150,
|
| 73 |
+
temperature=0.75,
|
| 74 |
+
top_p=0.9,
|
| 75 |
+
)
|
| 76 |
+
return {"description": description.strip()}
|
| 77 |
+
except HTTPException:
|
| 78 |
+
raise
|
| 79 |
+
except Exception as e:
|
| 80 |
+
print(f"Unexpected error in /enhance-description: {e}")
|
| 81 |
+
raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {str(e)}")
|
app/models/huggingface_service.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from transformers import pipeline, AutoTokenizer
|
| 2 |
+
import torch
|
| 3 |
+
from fastapi import HTTPException
|
| 4 |
+
import asyncio
|
| 5 |
+
|
| 6 |
+
class HuggingFaceTextGenerationService:
|
| 7 |
+
def __init__(self, model_name_or_path: str, device: str = None, task: str = "text-generation"):
|
| 8 |
+
self.model_name_or_path = model_name_or_path
|
| 9 |
+
self.task = task
|
| 10 |
+
self.pipeline = None
|
| 11 |
+
self.tokenizer = None
|
| 12 |
+
|
| 13 |
+
if device is None:
|
| 14 |
+
self.device_index = 0 if torch.cuda.is_available() else -1
|
| 15 |
+
elif device == "cuda" and torch.cuda.is_available():
|
| 16 |
+
self.device_index = 0
|
| 17 |
+
elif device == "cpu":
|
| 18 |
+
self.device_index = -1
|
| 19 |
+
else:
|
| 20 |
+
self.device_index = -1
|
| 21 |
+
|
| 22 |
+
if self.device_index == 0:
|
| 23 |
+
print("CUDA (GPU) is available. Using GPU.")
|
| 24 |
+
else:
|
| 25 |
+
print(f"Device set to use {'cpu' if self.device_index == -1 else f'cuda:{self.device_index}'}")
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
async def initialize(self):
|
| 29 |
+
try:
|
| 30 |
+
print(f"Initializing Hugging Face pipeline for model: {self.model_name_or_path} on device index: {self.device_index}")
|
| 31 |
+
self.tokenizer = await asyncio.to_thread(
|
| 32 |
+
AutoTokenizer.from_pretrained, self.model_name_or_path, trust_remote_code=True
|
| 33 |
+
)
|
| 34 |
+
self.pipeline = await asyncio.to_thread(
|
| 35 |
+
pipeline,
|
| 36 |
+
self.task,
|
| 37 |
+
model=self.model_name_or_path,
|
| 38 |
+
tokenizer=self.tokenizer,
|
| 39 |
+
device=self.device_index,
|
| 40 |
+
torch_dtype=torch.bfloat16 if self.device_index != -1 and torch.cuda.is_available() and torch.cuda.is_bf16_supported() else torch.float32,
|
| 41 |
+
trust_remote_code=True,
|
| 42 |
+
)
|
| 43 |
+
print(f"Pipeline for model {self.model_name_or_path} initialized successfully.")
|
| 44 |
+
except Exception as e:
|
| 45 |
+
print(f"Error initializing HuggingFace pipeline: {e}")
|
| 46 |
+
raise HTTPException(status_code=503, detail=f"LLM (HuggingFace) model could not be loaded: {str(e)}")
|
| 47 |
+
|
| 48 |
+
async def generate_text(self, prompt_text: str = None, chat_template_messages: list = None, max_new_tokens: int = 250, temperature: float = 0.7, top_p: float = 0.9, do_sample: bool = True, **kwargs) -> str:
|
| 49 |
+
if not self.pipeline or not self.tokenizer:
|
| 50 |
+
raise Exception("Pipeline is not initialized. Call initialize() first.")
|
| 51 |
+
|
| 52 |
+
formatted_prompt_input = ""
|
| 53 |
+
if chat_template_messages:
|
| 54 |
+
try:
|
| 55 |
+
formatted_prompt_input = self.tokenizer.apply_chat_template(
|
| 56 |
+
chat_template_messages,
|
| 57 |
+
tokenize=False,
|
| 58 |
+
add_generation_prompt=True
|
| 59 |
+
)
|
| 60 |
+
except Exception as e:
|
| 61 |
+
print(f"Could not apply chat template, falling back to raw prompt if available. Error: {e}")
|
| 62 |
+
if prompt_text:
|
| 63 |
+
formatted_prompt_input = prompt_text
|
| 64 |
+
else:
|
| 65 |
+
raise ValueError("Cannot generate text without a valid prompt or chat_template_messages.")
|
| 66 |
+
elif prompt_text:
|
| 67 |
+
formatted_prompt_input = prompt_text
|
| 68 |
+
else:
|
| 69 |
+
raise ValueError("Either prompt_text or chat_template_messages must be provided.")
|
| 70 |
+
|
| 71 |
+
try:
|
| 72 |
+
generated_outputs = await asyncio.to_thread(
|
| 73 |
+
self.pipeline,
|
| 74 |
+
formatted_prompt_input,
|
| 75 |
+
max_new_tokens=max_new_tokens,
|
| 76 |
+
do_sample=do_sample,
|
| 77 |
+
temperature=temperature,
|
| 78 |
+
top_p=top_p,
|
| 79 |
+
eos_token_id=self.tokenizer.eos_token_id,
|
| 80 |
+
pad_token_id=self.tokenizer.eos_token_id if self.tokenizer.pad_token_id is None else self.tokenizer.pad_token_id, # Common setting
|
| 81 |
+
**kwargs
|
| 82 |
+
)
|
| 83 |
+
|
| 84 |
+
if generated_outputs and isinstance(generated_outputs, list) and "generated_text" in generated_outputs[0]:
|
| 85 |
+
full_generated_sequence = generated_outputs[0]["generated_text"]
|
| 86 |
+
|
| 87 |
+
assistant_response = ""
|
| 88 |
+
if full_generated_sequence.startswith(formatted_prompt_input):
|
| 89 |
+
assistant_response = full_generated_sequence[len(formatted_prompt_input):]
|
| 90 |
+
else:
|
| 91 |
+
assistant_marker = "<|im_start|>assistant\n"
|
| 92 |
+
last_marker_pos = full_generated_sequence.rfind(assistant_marker)
|
| 93 |
+
if last_marker_pos != -1:
|
| 94 |
+
assistant_response = full_generated_sequence[last_marker_pos + len(assistant_marker):]
|
| 95 |
+
print("Warning: Used fallback parsing for assistant response.")
|
| 96 |
+
else:
|
| 97 |
+
print("Error: Could not isolate assistant response from the full generated sequence.")
|
| 98 |
+
assistant_response = full_generated_sequence
|
| 99 |
+
|
| 100 |
+
if assistant_response.endswith("<|im_end|>"):
|
| 101 |
+
assistant_response = assistant_response[:-len("<|im_end|>")]
|
| 102 |
+
|
| 103 |
+
return assistant_response.strip()
|
| 104 |
+
else:
|
| 105 |
+
print(f"Unexpected output format from pipeline: {generated_outputs}")
|
| 106 |
+
return "Error: Could not parse generated text from pipeline output."
|
| 107 |
+
|
| 108 |
+
except Exception as e:
|
| 109 |
+
print(f"Error during text generation with {self.model_name_or_path}: {e}")
|
| 110 |
+
raise HTTPException(status_code=500, detail=f"Error generating text: {str(e)}")
|
| 111 |
+
|
app/schemas/schemas.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pydantic import BaseModel
|
| 2 |
+
|
| 3 |
+
class CarData(BaseModel):
|
| 4 |
+
make: str
|
| 5 |
+
model: str
|
| 6 |
+
year: int
|
| 7 |
+
mileage: int
|
| 8 |
+
features: list[str]
|
| 9 |
+
condition: str
|
| 10 |
+
|
| 11 |
+
class EnhancedDescriptionResponse(BaseModel):
|
| 12 |
+
description: str
|
download_model.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This script is intended to be run in a Docker container with the Hugging Face token mounted as a secret.
|
| 2 |
+
from huggingface_hub import snapshot_download
|
| 3 |
+
from huggingface_hub.errors import HfHubHTTPError
|
| 4 |
+
import os
|
| 5 |
+
import sys
|
| 6 |
+
import traceback
|
| 7 |
+
|
| 8 |
+
def main():
|
| 9 |
+
token_path = '/run/secrets/huggingface_token'
|
| 10 |
+
model_dir_path = os.environ.get('MODEL_DIR')
|
| 11 |
+
repo_id_to_download = 'speakleash/Bielik-1.5B-v3.0-Instruct'
|
| 12 |
+
|
| 13 |
+
print(f'--- Python SCRIPT DEBUG: Target model directory: {model_dir_path}')
|
| 14 |
+
if not model_dir_path:
|
| 15 |
+
print('--- Python SCRIPT CRITICAL ERROR: MODEL_DIR environment variable not set!')
|
| 16 |
+
sys.exit(1)
|
| 17 |
+
|
| 18 |
+
token_value = None
|
| 19 |
+
try:
|
| 20 |
+
with open(token_path, 'r') as f:
|
| 21 |
+
token_value = f.read().strip()
|
| 22 |
+
print(f'--- Python SCRIPT DEBUG: Token file {token_path} read successfully.')
|
| 23 |
+
if token_value:
|
| 24 |
+
masked_token = f"{token_value[:4]}****{token_value[-4:] if len(token_value) > 4 else '(token too short)'}"
|
| 25 |
+
print(f'--- Python SCRIPT DEBUG: Token content (masked): {masked_token}')
|
| 26 |
+
if not token_value.startswith('hf_'):
|
| 27 |
+
print('--- Python SCRIPT WARNING: Token does not appear to start with hf_! Check token file content.')
|
| 28 |
+
else:
|
| 29 |
+
print('--- Python SCRIPT CRITICAL ERROR: Token file was empty or only whitespace!')
|
| 30 |
+
sys.exit(1)
|
| 31 |
+
except FileNotFoundError:
|
| 32 |
+
print(f'--- Python SCRIPT CRITICAL ERROR: Token secret file {token_path} not found! Ensure --mount is correct.')
|
| 33 |
+
sys.exit(1)
|
| 34 |
+
except Exception as e:
|
| 35 |
+
print(f'--- Python SCRIPT CRITICAL ERROR: Could not read token from {token_path}: {e}')
|
| 36 |
+
traceback.print_exc()
|
| 37 |
+
sys.exit(1)
|
| 38 |
+
|
| 39 |
+
try:
|
| 40 |
+
print(f'--- Python SCRIPT INFO: Calling snapshot_download for {repo_id_to_download}...')
|
| 41 |
+
snapshot_download(
|
| 42 |
+
repo_id=repo_id_to_download,
|
| 43 |
+
local_dir=model_dir_path,
|
| 44 |
+
token=token_value,
|
| 45 |
+
local_dir_use_symlinks=False,
|
| 46 |
+
resume_download=True
|
| 47 |
+
# Removed ignore_patterns for now to ensure no interference
|
| 48 |
+
)
|
| 49 |
+
print(f'--- Python SCRIPT INFO: snapshot_download completed for {repo_id_to_download}.')
|
| 50 |
+
except HfHubHTTPError as http_e:
|
| 51 |
+
print(f'--- Python SCRIPT ERROR: HfHubHTTPError during snapshot_download: {http_e}')
|
| 52 |
+
if http_e.response is not None:
|
| 53 |
+
print(f'--- Python SCRIPT ERROR: Response status: {http_e.response.status_code}')
|
| 54 |
+
print(f'--- Python SCRIPT ERROR: Response headers: {http_e.response.headers}')
|
| 55 |
+
try:
|
| 56 |
+
response_content = http_e.response.content.decode()
|
| 57 |
+
except UnicodeDecodeError:
|
| 58 |
+
response_content = str(http_e.response.content)
|
| 59 |
+
print(f'--- Python SCRIPT ERROR: Response content: {response_content}')
|
| 60 |
+
if http_e.request_id:
|
| 61 |
+
print(f'--- Python SCRIPT ERROR: Request ID: {http_e.request_id}')
|
| 62 |
+
sys.exit(1)
|
| 63 |
+
except Exception as e:
|
| 64 |
+
print(f'--- Python SCRIPT ERROR: Other Exception during snapshot_download: {e}')
|
| 65 |
+
traceback.print_exc()
|
| 66 |
+
sys.exit(1)
|
| 67 |
+
|
| 68 |
+
if __name__ == "__main__":
|
| 69 |
+
main()
|
requirements.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi
|
| 2 |
+
uvicorn[standard]
|
| 3 |
+
transformers[torch]
|
| 4 |
+
accelerate
|
start_container.ps1
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# PowerShell script to build and run the Docker container for your FastAPI service
|
| 2 |
+
|
| 3 |
+
# Set variables
|
| 4 |
+
$imageName = "bielik-fastapi-service"
|
| 5 |
+
$containerName = "bielik_app_instance"
|
| 6 |
+
$tokenFile = "my_hf_token.txt"
|
| 7 |
+
|
| 8 |
+
Write-Host "Building Docker image..."
|
| 9 |
+
docker build --secret id=huggingface_token,src=$tokenFile -t $imageName .
|
| 10 |
+
|
| 11 |
+
Write-Host "Stopping and removing any existing container named $containerName..."
|
| 12 |
+
docker stop $containerName | Out-Null 2>&1
|
| 13 |
+
|
| 14 |
+
docker rm $containerName | Out-Null 2>&1
|
| 15 |
+
|
| 16 |
+
Write-Host "Running new container..."
|
| 17 |
+
docker run -d --name $containerName -p 8000:8000 $imageName
|
| 18 |
+
|
| 19 |
+
Write-Host ""
|
| 20 |
+
Write-Host "$containerName should be starting up."
|
| 21 |
+
Write-Host "You can view logs with: docker logs $containerName -f"
|
| 22 |
+
Write-Host "To stop the container, run: docker stop $containerName"
|
| 23 |
+
Write-Host "The service will be available at http://127.0.0.1:8000"
|
start_container.sh
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
|
| 3 |
+
IMAGE_NAME="bielik-fastapi-service"
|
| 4 |
+
CONTAINER_NAME="bielik_app_instance"
|
| 5 |
+
TOKEN_FILE="my_hf_token.txt"
|
| 6 |
+
|
| 7 |
+
# Build the Docker image with Hugging Face token as a secret
|
| 8 |
+
echo "Building Docker image..."
|
| 9 |
+
DOCKER_BUILDKIT=1 docker build --secret id=huggingface_token,src=$TOKEN_FILE -t $IMAGE_NAME .
|
| 10 |
+
|
| 11 |
+
echo "Attempting to stop and remove existing container named $CONTAINER_NAME (if any)..."
|
| 12 |
+
docker stop $CONTAINER_NAME > /dev/null 2>&1 || true # Stop if running, ignore error if not
|
| 13 |
+
docker rm $CONTAINER_NAME > /dev/null 2>&1 || true # Remove if exists, ignore error if not
|
| 14 |
+
|
| 15 |
+
echo "Starting new $IMAGE_NAME container as $CONTAINER_NAME..."
|
| 16 |
+
docker run -d --name $CONTAINER_NAME -p 8000:8000 $IMAGE_NAME
|
| 17 |
+
# -d : Runs the container in detached mode (in the background)
|
| 18 |
+
# --name : Assigns a specific name to your running container instance
|
| 19 |
+
# -p 8000:8000 : Maps port 8000 on your host to port 8000 in the container
|
| 20 |
+
|
| 21 |
+
echo ""
|
| 22 |
+
echo "$CONTAINER_NAME should be starting up."
|
| 23 |
+
echo "You can view logs with: docker logs $CONTAINER_NAME -f"
|
| 24 |
+
echo "To stop the container, run: docker stop $CONTAINER_NAME"
|
| 25 |
+
echo "The service will be available at http://127.0.0.1:8000"
|