Spaces:
Running
on
Zero
Running
on
Zero
tries to download the model at build time
Browse files- .gitignore +82 -0
- README.md +178 -1
- app.py +303 -0
- build.py +113 -0
- config.yaml +51 -0
- deploy.py +181 -0
- download_model.py +118 -0
- download_model_advanced.py +206 -0
- requirements.txt +11 -0
- test_app.py +212 -0
- test_model_loading.py +81 -0
.gitignore
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Python
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*$py.class
|
| 5 |
+
*.so
|
| 6 |
+
.Python
|
| 7 |
+
build/
|
| 8 |
+
develop-eggs/
|
| 9 |
+
dist/
|
| 10 |
+
downloads/
|
| 11 |
+
eggs/
|
| 12 |
+
.eggs/
|
| 13 |
+
lib/
|
| 14 |
+
lib64/
|
| 15 |
+
parts/
|
| 16 |
+
sdist/
|
| 17 |
+
var/
|
| 18 |
+
wheels/
|
| 19 |
+
*.egg-info/
|
| 20 |
+
.installed.cfg
|
| 21 |
+
*.egg
|
| 22 |
+
MANIFEST
|
| 23 |
+
|
| 24 |
+
# PyTorch
|
| 25 |
+
*.pth
|
| 26 |
+
*.pt
|
| 27 |
+
|
| 28 |
+
# Model files (if downloaded locally)
|
| 29 |
+
models/
|
| 30 |
+
checkpoints/
|
| 31 |
+
int4/
|
| 32 |
+
*.safetensors
|
| 33 |
+
*.bin
|
| 34 |
+
*.json
|
| 35 |
+
!requirements.txt
|
| 36 |
+
!config.yaml
|
| 37 |
+
!app.py
|
| 38 |
+
!README.md
|
| 39 |
+
!test_app.py
|
| 40 |
+
!deploy.py
|
| 41 |
+
|
| 42 |
+
# Hugging Face cache
|
| 43 |
+
.cache/
|
| 44 |
+
.huggingface/
|
| 45 |
+
transformers_cache/
|
| 46 |
+
|
| 47 |
+
# Logs
|
| 48 |
+
*.log
|
| 49 |
+
logs/
|
| 50 |
+
|
| 51 |
+
# Environment
|
| 52 |
+
.env
|
| 53 |
+
.venv
|
| 54 |
+
env/
|
| 55 |
+
venv/
|
| 56 |
+
ENV/
|
| 57 |
+
env.bak/
|
| 58 |
+
venv.bak/
|
| 59 |
+
|
| 60 |
+
# IDE
|
| 61 |
+
.vscode/
|
| 62 |
+
.idea/
|
| 63 |
+
*.swp
|
| 64 |
+
*.swo
|
| 65 |
+
*~
|
| 66 |
+
|
| 67 |
+
# OS
|
| 68 |
+
.DS_Store
|
| 69 |
+
.DS_Store?
|
| 70 |
+
._*
|
| 71 |
+
.Spotlight-V100
|
| 72 |
+
.Trashes
|
| 73 |
+
ehthumbs.db
|
| 74 |
+
Thumbs.db
|
| 75 |
+
|
| 76 |
+
# Gradio
|
| 77 |
+
gradio_cached_examples/
|
| 78 |
+
flagged/
|
| 79 |
+
|
| 80 |
+
# Temporary files
|
| 81 |
+
*.tmp
|
| 82 |
+
*.temp
|
README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
---
|
| 2 |
title: Petite LLM 3
|
| 3 |
-
emoji:
|
| 4 |
colorFrom: green
|
| 5 |
colorTo: purple
|
| 6 |
sdk: gradio
|
|
@@ -11,4 +11,181 @@ license: mit
|
|
| 11 |
short_description: Smollm3 for French Understanding
|
| 12 |
---
|
| 13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
| 1 |
---
|
| 2 |
title: Petite LLM 3
|
| 3 |
+
emoji: ππ»
|
| 4 |
colorFrom: green
|
| 5 |
colorTo: purple
|
| 6 |
sdk: gradio
|
|
|
|
| 11 |
short_description: Smollm3 for French Understanding
|
| 12 |
---
|
| 13 |
|
| 14 |
+
# π€ Petite Elle L'Aime 3 - Chat Interface
|
| 15 |
+
|
| 16 |
+
A complete Gradio application for the [Petite Elle L'Aime 3](https://huggingface.co/Tonic/petite-elle-L-aime-3-sft) model, featuring the int4 quantized version for efficient CPU deployment.
|
| 17 |
+
|
| 18 |
+
## π Features
|
| 19 |
+
|
| 20 |
+
- **Multilingual Support**: English, French, Italian, Portuguese, Chinese, Arabic
|
| 21 |
+
- **Int4 Quantization**: Optimized for CPU deployment with ~50% memory reduction
|
| 22 |
+
- **Interactive Chat Interface**: Real-time conversation with the model
|
| 23 |
+
- **Customizable System Prompt**: Define the assistant's personality and behavior
|
| 24 |
+
- **Thinking Mode**: Enable reasoning mode with thinking tags
|
| 25 |
+
- **Responsive Design**: Modern UI following the reference layout
|
| 26 |
+
- **Chat Template Integration**: Proper Jinja template formatting
|
| 27 |
+
- **Automatic Model Download**: Downloads int4 model at build time
|
| 28 |
+
|
| 29 |
+
## π Model Information
|
| 30 |
+
|
| 31 |
+
- **Base Model**: SmolLM3-3B
|
| 32 |
+
- **Parameters**: ~3B
|
| 33 |
+
- **Context Length**: 128k
|
| 34 |
+
- **Quantization**: int4 (CPU optimized)
|
| 35 |
+
- **Memory Reduction**: ~50%
|
| 36 |
+
- **Languages**: English, French, Italian, Portuguese, Chinese, Arabic
|
| 37 |
+
|
| 38 |
+
## π οΈ Installation
|
| 39 |
+
|
| 40 |
+
1. Clone this repository:
|
| 41 |
+
```bash
|
| 42 |
+
git clone <repository-url>
|
| 43 |
+
cd Petite-LLM-3
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
2. Install dependencies:
|
| 47 |
+
```bash
|
| 48 |
+
pip install -r requirements.txt
|
| 49 |
+
```
|
| 50 |
+
|
| 51 |
+
## π Usage
|
| 52 |
+
|
| 53 |
+
### Local Development
|
| 54 |
+
|
| 55 |
+
Run the application locally:
|
| 56 |
+
```bash
|
| 57 |
+
python app.py
|
| 58 |
+
```
|
| 59 |
+
|
| 60 |
+
The application will be available at `http://localhost:7860`
|
| 61 |
+
|
| 62 |
+
### Hugging Face Spaces
|
| 63 |
+
|
| 64 |
+
This application is configured for deployment on Hugging Face Spaces with automatic model download:
|
| 65 |
+
|
| 66 |
+
1. **Build Process**: The `build.py` script automatically downloads the int4 model during Space build
|
| 67 |
+
2. **Model Loading**: Uses local model files when available, falls back to Hugging Face download
|
| 68 |
+
3. **Caching**: Model files are cached for faster subsequent runs
|
| 69 |
+
|
| 70 |
+
## ποΈ Interface Features
|
| 71 |
+
|
| 72 |
+
### Layout Structure
|
| 73 |
+
The interface follows the reference layout with:
|
| 74 |
+
- **Title Section**: Main heading and description
|
| 75 |
+
- **Information Panels**: Features and model information
|
| 76 |
+
- **Input Section**: Context and user input areas
|
| 77 |
+
- **Advanced Settings**: Collapsible parameter controls
|
| 78 |
+
- **Chat Interface**: Real-time conversation display
|
| 79 |
+
|
| 80 |
+
### System Prompt
|
| 81 |
+
- **Default**: "Tu es TonicIA, un assistant francophone rigoureux et bienveillant."
|
| 82 |
+
- **Editable**: Users can customize the system prompt to define the assistant's personality
|
| 83 |
+
- **Real-time**: Changes take effect immediately for new conversations
|
| 84 |
+
|
| 85 |
+
### Generation Parameters
|
| 86 |
+
- **Max Length**: Maximum number of tokens to generate (64-2048)
|
| 87 |
+
- **Temperature**: Controls randomness in generation (0.01-1.0)
|
| 88 |
+
- **Top-p**: Nucleus sampling parameter (0.1-1.0)
|
| 89 |
+
- **Enable Thinking**: Enable reasoning mode with thinking tags
|
| 90 |
+
- **Advanced Settings**: Collapsible panel for fine-tuning
|
| 91 |
+
|
| 92 |
+
## π§ Technical Details
|
| 93 |
+
|
| 94 |
+
### Model Loading Strategy
|
| 95 |
+
The application uses a smart loading strategy:
|
| 96 |
+
|
| 97 |
+
1. **Local Check**: First checks if int4 model files exist locally
|
| 98 |
+
2. **Local Loading**: If available, loads from `./int4` folder
|
| 99 |
+
3. **Fallback Download**: If not available, downloads from Hugging Face
|
| 100 |
+
4. **Tokenizer**: Always uses main repo for chat template and configuration
|
| 101 |
+
|
| 102 |
+
### Build Process
|
| 103 |
+
For Hugging Face Spaces deployment:
|
| 104 |
+
|
| 105 |
+
1. **Build Script**: `build.py` runs during Space build
|
| 106 |
+
2. **Model Download**: `download_model.py` downloads int4 model files
|
| 107 |
+
3. **Local Storage**: Model files stored in `./int4` directory
|
| 108 |
+
4. **Fast Loading**: Subsequent runs use local files
|
| 109 |
+
|
| 110 |
+
### Chat Template Integration
|
| 111 |
+
The application uses the custom chat template from the model, which supports:
|
| 112 |
+
- System prompt integration
|
| 113 |
+
- User and assistant message formatting
|
| 114 |
+
- Thinking mode with `<think>` tags
|
| 115 |
+
- Proper conversation flow management
|
| 116 |
+
|
| 117 |
+
### Memory Optimization
|
| 118 |
+
- Uses int4 quantization for reduced memory footprint
|
| 119 |
+
- Automatic device detection (CUDA/CPU)
|
| 120 |
+
- Efficient tokenization and generation
|
| 121 |
+
|
| 122 |
+
## π Example Usage
|
| 123 |
+
|
| 124 |
+
1. **Basic Conversation**:
|
| 125 |
+
- Add context in the system prompt area
|
| 126 |
+
- Type your message in the user input box
|
| 127 |
+
- Click the generate button to start chatting
|
| 128 |
+
|
| 129 |
+
2. **Customizing System Prompt**:
|
| 130 |
+
- Edit the context in the dedicated text area
|
| 131 |
+
- Changes apply to new messages immediately
|
| 132 |
+
- Example: "Tu es un expert en programmation Python."
|
| 133 |
+
|
| 134 |
+
3. **Advanced Settings**:
|
| 135 |
+
- Check the "Advanced Settings" checkbox
|
| 136 |
+
- Adjust generation parameters as needed
|
| 137 |
+
- Enable/disable thinking mode
|
| 138 |
+
|
| 139 |
+
4. **Real-time Chat**:
|
| 140 |
+
- Messages appear in the chat interface
|
| 141 |
+
- Conversation history is maintained
|
| 142 |
+
- Responses are generated using the model's chat template
|
| 143 |
+
|
| 144 |
+
## π Troubleshooting
|
| 145 |
+
|
| 146 |
+
### Common Issues
|
| 147 |
+
|
| 148 |
+
1. **Model Loading Errors**:
|
| 149 |
+
- Ensure you have sufficient RAM (8GB+ recommended)
|
| 150 |
+
- Check your internet connection for model download
|
| 151 |
+
- Verify all dependencies are installed
|
| 152 |
+
|
| 153 |
+
2. **Generation Errors**:
|
| 154 |
+
- Try reducing the "Max Length" parameter
|
| 155 |
+
- Adjust temperature and top-p values
|
| 156 |
+
- Check the console for detailed error messages
|
| 157 |
+
|
| 158 |
+
3. **Performance Issues**:
|
| 159 |
+
- The int4 model is optimized for CPU but may be slower than GPU versions
|
| 160 |
+
- Consider using a machine with more RAM for better performance
|
| 161 |
+
|
| 162 |
+
4. **System Prompt Issues**:
|
| 163 |
+
- Ensure the system prompt is not too long (max 1000 characters)
|
| 164 |
+
- Check that the prompt follows the expected format
|
| 165 |
+
|
| 166 |
+
5. **Build Process Issues**:
|
| 167 |
+
- Check that `download_model.py` runs successfully
|
| 168 |
+
- Verify that model files are downloaded to `./int4` directory
|
| 169 |
+
- Ensure sufficient storage space for model files
|
| 170 |
+
|
| 171 |
+
## π License
|
| 172 |
+
|
| 173 |
+
This project is licensed under the MIT License. The underlying model is licensed under Apache 2.0.
|
| 174 |
+
|
| 175 |
+
## π Acknowledgments
|
| 176 |
+
|
| 177 |
+
- **Model**: [Tonic/petite-elle-L-aime-3-sft](https://huggingface.co/Tonic/petite-elle-L-aime-3-sft)
|
| 178 |
+
- **Base Model**: SmolLM3-3B by HuggingFaceTB
|
| 179 |
+
- **Training Data**: legmlai/openhermes-fr
|
| 180 |
+
- **Framework**: Gradio, Transformers, PyTorch
|
| 181 |
+
- **Layout Reference**: [Tonic/Nvidia-OpenReasoning](https://huggingface.co/spaces/Tonic/Nvidia-OpenReasoning)
|
| 182 |
+
|
| 183 |
+
## π Links
|
| 184 |
+
|
| 185 |
+
- [Model on Hugging Face](https://huggingface.co/Tonic/petite-elle-L-aime-3-sft)
|
| 186 |
+
- [Chat Template](https://huggingface.co/Tonic/petite-elle-L-aime-3-sft/blob/main/chat_template.jinja)
|
| 187 |
+
- [Original App Reference](https://huggingface.co/spaces/Tonic/Nvidia-OpenReasoning)
|
| 188 |
+
|
| 189 |
+
---
|
| 190 |
+
|
| 191 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
app.py
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
import torch
|
| 3 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
| 4 |
+
import re
|
| 5 |
+
import json
|
| 6 |
+
from typing import List, Dict, Any, Optional
|
| 7 |
+
import logging
|
| 8 |
+
import spaces
|
| 9 |
+
import os
|
| 10 |
+
|
| 11 |
+
# Configure logging
|
| 12 |
+
logging.basicConfig(level=logging.INFO)
|
| 13 |
+
logger = logging.getLogger(__name__)
|
| 14 |
+
|
| 15 |
+
# Model configuration
|
| 16 |
+
MAIN_MODEL_ID = "Tonic/petite-elle-L-aime-3-sft" # Main repo for config and chat template
|
| 17 |
+
INT4_MODEL_ID = "Tonic/petite-elle-L-aime-3-sft/int4" # Int4 quantized model
|
| 18 |
+
LOCAL_MODEL_PATH = "./int4" # Local int4 weights
|
| 19 |
+
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
|
| 20 |
+
|
| 21 |
+
# Global variables for model and tokenizer
|
| 22 |
+
model = None
|
| 23 |
+
tokenizer = None
|
| 24 |
+
|
| 25 |
+
# Default system prompt
|
| 26 |
+
DEFAULT_SYSTEM_PROMPT = "Tu es TonicIA, un assistant francophone rigoureux et bienveillant."
|
| 27 |
+
|
| 28 |
+
# Title and description content
|
| 29 |
+
title = "# π€ Petite Elle L'Aime 3 - Chat Interface"
|
| 30 |
+
description = "A fine-tuned version of SmolLM3-3B optimized for French and multilingual conversations. This is the int4 quantized version for efficient CPU deployment."
|
| 31 |
+
presentation1 = """
|
| 32 |
+
### π― Features
|
| 33 |
+
- **Multilingual Support**: English, French, Italian, Portuguese, Chinese, Arabic
|
| 34 |
+
- **Int4 Quantization**: Optimized for CPU deployment with ~50% memory reduction
|
| 35 |
+
- **Interactive Chat Interface**: Real-time conversation with the model
|
| 36 |
+
- **Customizable System Prompt**: Define the assistant's personality and behavior
|
| 37 |
+
- **Thinking Mode**: Enable reasoning mode with thinking tags
|
| 38 |
+
"""
|
| 39 |
+
presentation2 = """
|
| 40 |
+
### π Model Information
|
| 41 |
+
- **Base Model**: SmolLM3-3B
|
| 42 |
+
- **Parameters**: ~3B
|
| 43 |
+
- **Context Length**: 128k
|
| 44 |
+
- **Languages**: English, French, Italian, Portuguese, Chinese, Arabic
|
| 45 |
+
- **Device**: CPU optimized
|
| 46 |
+
- **Quantization**: int4
|
| 47 |
+
"""
|
| 48 |
+
joinus = """
|
| 49 |
+
### π Quick Start
|
| 50 |
+
1. Add context in the system prompt
|
| 51 |
+
2. Type your message
|
| 52 |
+
3. Click generate to start chatting
|
| 53 |
+
4. Use advanced settings for fine-tuning
|
| 54 |
+
"""
|
| 55 |
+
|
| 56 |
+
def check_local_model():
|
| 57 |
+
"""Check if local int4 model files exist"""
|
| 58 |
+
required_files = [
|
| 59 |
+
"config.json",
|
| 60 |
+
"pytorch_model.bin",
|
| 61 |
+
"tokenizer.json",
|
| 62 |
+
"tokenizer_config.json"
|
| 63 |
+
]
|
| 64 |
+
|
| 65 |
+
for file in required_files:
|
| 66 |
+
file_path = os.path.join(LOCAL_MODEL_PATH, file)
|
| 67 |
+
if not os.path.exists(file_path):
|
| 68 |
+
logger.warning(f"Missing required file: {file_path}")
|
| 69 |
+
return False
|
| 70 |
+
|
| 71 |
+
logger.info("All required model files found locally")
|
| 72 |
+
return True
|
| 73 |
+
|
| 74 |
+
def load_model():
|
| 75 |
+
"""Load the model and tokenizer"""
|
| 76 |
+
global model, tokenizer
|
| 77 |
+
|
| 78 |
+
try:
|
| 79 |
+
# Check if local model exists (downloaded during build)
|
| 80 |
+
if check_local_model():
|
| 81 |
+
logger.info(f"Loading tokenizer from {LOCAL_MODEL_PATH}")
|
| 82 |
+
tokenizer = AutoTokenizer.from_pretrained(LOCAL_MODEL_PATH)
|
| 83 |
+
|
| 84 |
+
logger.info(f"Loading int4 model from {LOCAL_MODEL_PATH}")
|
| 85 |
+
model = AutoModelForCausalLM.from_pretrained(
|
| 86 |
+
LOCAL_MODEL_PATH,
|
| 87 |
+
device_map="auto" if DEVICE == "cuda" else "cpu",
|
| 88 |
+
torch_dtype=torch.bfloat16,
|
| 89 |
+
trust_remote_code=True
|
| 90 |
+
)
|
| 91 |
+
else:
|
| 92 |
+
logger.info(f"Local model not found, loading from {MAIN_MODEL_ID}")
|
| 93 |
+
|
| 94 |
+
# Load tokenizer from main repo (for chat template and config)
|
| 95 |
+
tokenizer = AutoTokenizer.from_pretrained(MAIN_MODEL_ID)
|
| 96 |
+
|
| 97 |
+
logger.info(f"Loading int4 model from {INT4_MODEL_ID}")
|
| 98 |
+
|
| 99 |
+
# Load model with int4 quantization from Hugging Face
|
| 100 |
+
model = AutoModelForCausalLM.from_pretrained(
|
| 101 |
+
INT4_MODEL_ID,
|
| 102 |
+
device_map="auto" if DEVICE == "cuda" else "cpu",
|
| 103 |
+
torch_dtype=torch.bfloat16,
|
| 104 |
+
trust_remote_code=True
|
| 105 |
+
)
|
| 106 |
+
|
| 107 |
+
# Set pad token if not present
|
| 108 |
+
if tokenizer.pad_token_id is None:
|
| 109 |
+
tokenizer.pad_token_id = tokenizer.eos_token_id
|
| 110 |
+
|
| 111 |
+
logger.info("Model loaded successfully")
|
| 112 |
+
return True
|
| 113 |
+
|
| 114 |
+
except Exception as e:
|
| 115 |
+
logger.error(f"Error loading model: {e}")
|
| 116 |
+
return False
|
| 117 |
+
|
| 118 |
+
def create_prompt(system_message, user_message, enable_thinking=True):
|
| 119 |
+
"""Create prompt using the model's chat template"""
|
| 120 |
+
try:
|
| 121 |
+
# Prepare messages for the template
|
| 122 |
+
formatted_messages = []
|
| 123 |
+
|
| 124 |
+
# Add system message if provided
|
| 125 |
+
if system_message and system_message.strip():
|
| 126 |
+
formatted_messages.append({"role": "system", "content": system_message})
|
| 127 |
+
|
| 128 |
+
# Add user message
|
| 129 |
+
formatted_messages.append({"role": "user", "content": user_message})
|
| 130 |
+
|
| 131 |
+
# Apply the chat template
|
| 132 |
+
prompt = tokenizer.apply_chat_template(
|
| 133 |
+
formatted_messages,
|
| 134 |
+
tokenize=False,
|
| 135 |
+
add_generation_prompt=True,
|
| 136 |
+
enable_thinking=enable_thinking
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
+
# Add /no_think to the end of prompt when thinking is disabled
|
| 140 |
+
if not enable_thinking:
|
| 141 |
+
prompt += " /no_think"
|
| 142 |
+
|
| 143 |
+
return prompt
|
| 144 |
+
|
| 145 |
+
except Exception as e:
|
| 146 |
+
logger.error(f"Error creating prompt: {e}")
|
| 147 |
+
return ""
|
| 148 |
+
|
| 149 |
+
@spaces.GPU(duration=94)
|
| 150 |
+
def generate_response(message, history, system_message, max_tokens, temperature, top_p, do_sample, enable_thinking=True):
|
| 151 |
+
"""Generate response using the model"""
|
| 152 |
+
global model, tokenizer
|
| 153 |
+
|
| 154 |
+
if model is None or tokenizer is None:
|
| 155 |
+
return "Error: Model not loaded. Please wait for the model to load."
|
| 156 |
+
|
| 157 |
+
try:
|
| 158 |
+
# Create prompt using chat template
|
| 159 |
+
full_prompt = create_prompt(system_message, message, enable_thinking)
|
| 160 |
+
|
| 161 |
+
if not full_prompt:
|
| 162 |
+
return "Error: Failed to create prompt."
|
| 163 |
+
|
| 164 |
+
# Tokenize the input
|
| 165 |
+
inputs = tokenizer(full_prompt, return_tensors="pt", padding=True, truncation=True)
|
| 166 |
+
|
| 167 |
+
# Move to device
|
| 168 |
+
if DEVICE == "cuda":
|
| 169 |
+
inputs = {k: v.cuda() for k, v in inputs.items()}
|
| 170 |
+
|
| 171 |
+
# Generate response
|
| 172 |
+
with torch.no_grad():
|
| 173 |
+
output_ids = model.generate(
|
| 174 |
+
inputs['input_ids'],
|
| 175 |
+
max_new_tokens=max_tokens,
|
| 176 |
+
temperature=temperature,
|
| 177 |
+
top_p=top_p,
|
| 178 |
+
do_sample=do_sample,
|
| 179 |
+
attention_mask=inputs['attention_mask'],
|
| 180 |
+
pad_token_id=tokenizer.eos_token_id,
|
| 181 |
+
eos_token_id=tokenizer.eos_token_id
|
| 182 |
+
)
|
| 183 |
+
|
| 184 |
+
# Decode the response
|
| 185 |
+
response = tokenizer.decode(output_ids[0], skip_special_tokens=True)
|
| 186 |
+
|
| 187 |
+
# Extract only the new response (remove the input prompt)
|
| 188 |
+
assistant_response = response[len(full_prompt):].strip()
|
| 189 |
+
|
| 190 |
+
# Clean up the response - only remove special tokens, preserve thinking tags when enabled
|
| 191 |
+
assistant_response = re.sub(r'<\|im_start\|>.*?<\|im_end\|>', '', assistant_response, flags=re.DOTALL)
|
| 192 |
+
|
| 193 |
+
# Only remove thinking tags if thinking mode is disabled
|
| 194 |
+
if not enable_thinking:
|
| 195 |
+
assistant_response = re.sub(r'<think>.*?</think>', '', assistant_response, flags=re.DOTALL)
|
| 196 |
+
|
| 197 |
+
assistant_response = assistant_response.strip()
|
| 198 |
+
|
| 199 |
+
return assistant_response
|
| 200 |
+
|
| 201 |
+
except Exception as e:
|
| 202 |
+
logger.error(f"Error generating response: {e}")
|
| 203 |
+
return f"Error generating response: {str(e)}"
|
| 204 |
+
|
| 205 |
+
def user(user_message, history):
|
| 206 |
+
"""Add user message to history"""
|
| 207 |
+
return "", history + [[user_message, None]]
|
| 208 |
+
|
| 209 |
+
def bot(history, system_prompt, max_length, temperature, top_p, advanced_checkbox, enable_thinking):
|
| 210 |
+
"""Generate bot response"""
|
| 211 |
+
user_message = history[-1][0]
|
| 212 |
+
do_sample = advanced_checkbox
|
| 213 |
+
bot_message = generate_response(user_message, history, system_prompt, max_length, temperature, top_p, do_sample, enable_thinking)
|
| 214 |
+
history[-1][1] = bot_message
|
| 215 |
+
return history
|
| 216 |
+
|
| 217 |
+
# Load model on startup
|
| 218 |
+
logger.info("Starting model loading process...")
|
| 219 |
+
load_model()
|
| 220 |
+
|
| 221 |
+
# Create Gradio interface
|
| 222 |
+
with gr.Blocks() as demo:
|
| 223 |
+
with gr.Row():
|
| 224 |
+
gr.Markdown(title)
|
| 225 |
+
with gr.Row():
|
| 226 |
+
gr.Markdown(description)
|
| 227 |
+
with gr.Row():
|
| 228 |
+
with gr.Column(scale=1):
|
| 229 |
+
with gr.Group():
|
| 230 |
+
gr.Markdown(presentation1)
|
| 231 |
+
with gr.Column(scale=1):
|
| 232 |
+
with gr.Group():
|
| 233 |
+
gr.Markdown(presentation2)
|
| 234 |
+
with gr.Row():
|
| 235 |
+
with gr.Column(scale=1):
|
| 236 |
+
with gr.Group():
|
| 237 |
+
gr.Markdown(joinus)
|
| 238 |
+
with gr.Column(scale=1):
|
| 239 |
+
pass # Empty column for balance
|
| 240 |
+
|
| 241 |
+
with gr.Row():
|
| 242 |
+
with gr.Column(scale=2):
|
| 243 |
+
system_prompt = gr.TextArea(
|
| 244 |
+
label="π Context",
|
| 245 |
+
placeholder="Tu es TonicIA, un assistant francophone rigoureux et bienveillant.",
|
| 246 |
+
lines=5,
|
| 247 |
+
value=DEFAULT_SYSTEM_PROMPT
|
| 248 |
+
)
|
| 249 |
+
user_input = gr.TextArea(
|
| 250 |
+
label="π€·π»ββοΈ User Input",
|
| 251 |
+
placeholder="Hi there my name is Tonic!",
|
| 252 |
+
lines=2
|
| 253 |
+
)
|
| 254 |
+
advanced_checkbox = gr.Checkbox(label="π§ͺ Advanced Settings", value=False)
|
| 255 |
+
with gr.Column(visible=False) as advanced_settings:
|
| 256 |
+
max_length = gr.Slider(
|
| 257 |
+
label="π Max Length",
|
| 258 |
+
minimum=64,
|
| 259 |
+
maximum=2048,
|
| 260 |
+
value=512,
|
| 261 |
+
step=64
|
| 262 |
+
)
|
| 263 |
+
temperature = gr.Slider(
|
| 264 |
+
label="π‘οΈ Temperature",
|
| 265 |
+
minimum=0.01,
|
| 266 |
+
maximum=1.0,
|
| 267 |
+
value=0.7,
|
| 268 |
+
step=0.01
|
| 269 |
+
)
|
| 270 |
+
top_p = gr.Slider(
|
| 271 |
+
label="βοΈ Top-p (Nucleus Sampling)",
|
| 272 |
+
minimum=0.1,
|
| 273 |
+
maximum=1.0,
|
| 274 |
+
value=0.9,
|
| 275 |
+
step=0.01
|
| 276 |
+
)
|
| 277 |
+
enable_thinking = gr.Checkbox(label="Enable Thinking Mode", value=True)
|
| 278 |
+
|
| 279 |
+
generate_button = gr.Button(value="π€ Petite Elle L'Aime 3")
|
| 280 |
+
|
| 281 |
+
with gr.Column(scale=2):
|
| 282 |
+
chatbot = gr.Chatbot(label="π€ Petite Elle L'Aime 3")
|
| 283 |
+
|
| 284 |
+
generate_button.click(
|
| 285 |
+
user,
|
| 286 |
+
[user_input, chatbot],
|
| 287 |
+
[user_input, chatbot],
|
| 288 |
+
queue=False
|
| 289 |
+
).then(
|
| 290 |
+
bot,
|
| 291 |
+
[chatbot, system_prompt, max_length, temperature, top_p, advanced_checkbox, enable_thinking],
|
| 292 |
+
chatbot
|
| 293 |
+
)
|
| 294 |
+
|
| 295 |
+
advanced_checkbox.change(
|
| 296 |
+
fn=lambda x: gr.update(visible=x),
|
| 297 |
+
inputs=[advanced_checkbox],
|
| 298 |
+
outputs=[advanced_settings]
|
| 299 |
+
)
|
| 300 |
+
|
| 301 |
+
if __name__ == "__main__":
|
| 302 |
+
demo.queue()
|
| 303 |
+
demo.launch(ssr_mode=False, mcp_server=True)
|
build.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Build script for Hugging Face Spaces - downloads model files at build time
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import os
|
| 7 |
+
import sys
|
| 8 |
+
import subprocess
|
| 9 |
+
import logging
|
| 10 |
+
|
| 11 |
+
# Configure logging
|
| 12 |
+
logging.basicConfig(level=logging.INFO)
|
| 13 |
+
logger = logging.getLogger(__name__)
|
| 14 |
+
|
| 15 |
+
def run_download_script():
|
| 16 |
+
"""Run the model download script"""
|
| 17 |
+
try:
|
| 18 |
+
logger.info("Running advanced model download script...")
|
| 19 |
+
|
| 20 |
+
# Try the advanced download script first
|
| 21 |
+
result = subprocess.run([sys.executable, "download_model_advanced.py"],
|
| 22 |
+
capture_output=True, text=True, check=True)
|
| 23 |
+
logger.info("Advanced model download completed successfully")
|
| 24 |
+
logger.info(result.stdout)
|
| 25 |
+
return True
|
| 26 |
+
|
| 27 |
+
except subprocess.CalledProcessError as e:
|
| 28 |
+
logger.warning(f"Advanced download failed: {e}")
|
| 29 |
+
logger.warning("Falling back to basic download script...")
|
| 30 |
+
|
| 31 |
+
try:
|
| 32 |
+
# Fallback to basic download script
|
| 33 |
+
result = subprocess.run([sys.executable, "download_model.py"],
|
| 34 |
+
capture_output=True, text=True, check=True)
|
| 35 |
+
logger.info("Basic model download completed successfully")
|
| 36 |
+
logger.info(result.stdout)
|
| 37 |
+
return True
|
| 38 |
+
|
| 39 |
+
except subprocess.CalledProcessError as e2:
|
| 40 |
+
logger.error(f"Basic download also failed: {e2}")
|
| 41 |
+
logger.error(e2.stderr)
|
| 42 |
+
return False
|
| 43 |
+
|
| 44 |
+
def verify_build():
|
| 45 |
+
"""Verify that the build was successful"""
|
| 46 |
+
try:
|
| 47 |
+
logger.info("Verifying build results...")
|
| 48 |
+
|
| 49 |
+
# Check if int4 directory exists
|
| 50 |
+
if not os.path.exists("./int4"):
|
| 51 |
+
logger.error("int4 directory not found")
|
| 52 |
+
return False
|
| 53 |
+
|
| 54 |
+
# Check for essential files
|
| 55 |
+
essential_files = [
|
| 56 |
+
"config.json",
|
| 57 |
+
"pytorch_model.bin",
|
| 58 |
+
"tokenizer.json",
|
| 59 |
+
"tokenizer_config.json"
|
| 60 |
+
]
|
| 61 |
+
|
| 62 |
+
missing_files = []
|
| 63 |
+
for file in essential_files:
|
| 64 |
+
file_path = os.path.join("./int4", file)
|
| 65 |
+
if not os.path.exists(file_path):
|
| 66 |
+
missing_files.append(file)
|
| 67 |
+
|
| 68 |
+
if missing_files:
|
| 69 |
+
logger.error(f"Missing essential files: {missing_files}")
|
| 70 |
+
return False
|
| 71 |
+
|
| 72 |
+
# Check file sizes
|
| 73 |
+
total_size = 0
|
| 74 |
+
for file in essential_files:
|
| 75 |
+
file_path = os.path.join("./int4", file)
|
| 76 |
+
if os.path.exists(file_path):
|
| 77 |
+
file_size = os.path.getsize(file_path)
|
| 78 |
+
total_size += file_size
|
| 79 |
+
logger.info(f"β
{file}: {file_size} bytes")
|
| 80 |
+
|
| 81 |
+
logger.info(f"Total model size: {total_size / (1024*1024):.2f} MB")
|
| 82 |
+
|
| 83 |
+
if total_size < 1000000: # Less than 1MB
|
| 84 |
+
logger.warning("Model files seem too small")
|
| 85 |
+
return False
|
| 86 |
+
|
| 87 |
+
logger.info("Build verification completed successfully")
|
| 88 |
+
return True
|
| 89 |
+
|
| 90 |
+
except Exception as e:
|
| 91 |
+
logger.error(f"Error verifying build: {e}")
|
| 92 |
+
return False
|
| 93 |
+
|
| 94 |
+
def main():
|
| 95 |
+
"""Main build function"""
|
| 96 |
+
logger.info("Starting Hugging Face Space build process...")
|
| 97 |
+
|
| 98 |
+
# Run the model download script
|
| 99 |
+
if run_download_script():
|
| 100 |
+
# Verify the build
|
| 101 |
+
if verify_build():
|
| 102 |
+
logger.info("Build process completed successfully")
|
| 103 |
+
return True
|
| 104 |
+
else:
|
| 105 |
+
logger.error("Build verification failed")
|
| 106 |
+
return False
|
| 107 |
+
else:
|
| 108 |
+
logger.error("Model download failed")
|
| 109 |
+
return False
|
| 110 |
+
|
| 111 |
+
if __name__ == "__main__":
|
| 112 |
+
success = main()
|
| 113 |
+
sys.exit(0 if success else 1)
|
config.yaml
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Configuration file for Petite Elle L'Aime 3 Gradio Application
|
| 2 |
+
|
| 3 |
+
# Model Configuration
|
| 4 |
+
model:
|
| 5 |
+
main_repo: "Tonic/petite-elle-L-aime-3-sft" # Main repo for config and chat template
|
| 6 |
+
int4_repo: "Tonic/petite-elle-L-aime-3-sft/int4" # Int4 quantized model from HF
|
| 7 |
+
device: "auto" # "cuda", "cpu", or "auto"
|
| 8 |
+
torch_dtype: "bfloat16"
|
| 9 |
+
trust_remote_code: true
|
| 10 |
+
|
| 11 |
+
# System Prompt Configuration
|
| 12 |
+
system_prompt:
|
| 13 |
+
default: "Tu es TonicIA, un assistant francophone rigoureux et bienveillant."
|
| 14 |
+
editable: true
|
| 15 |
+
max_length: 1000
|
| 16 |
+
|
| 17 |
+
# Generation Parameters (defaults)
|
| 18 |
+
generation:
|
| 19 |
+
max_new_tokens: 512
|
| 20 |
+
temperature: 0.7
|
| 21 |
+
top_p: 0.9
|
| 22 |
+
top_k: 50
|
| 23 |
+
repetition_penalty: 1.1
|
| 24 |
+
do_sample: true
|
| 25 |
+
|
| 26 |
+
# Chat Configuration
|
| 27 |
+
chat:
|
| 28 |
+
enable_thinking: true
|
| 29 |
+
max_history_length: 50
|
| 30 |
+
|
| 31 |
+
# UI Configuration
|
| 32 |
+
ui:
|
| 33 |
+
title: "Petite Elle L'Aime 3 - Chat Interface"
|
| 34 |
+
theme: "soft"
|
| 35 |
+
server_port: 7860
|
| 36 |
+
server_name: "0.0.0.0"
|
| 37 |
+
share: false
|
| 38 |
+
show_error: true
|
| 39 |
+
layout: "responsive"
|
| 40 |
+
|
| 41 |
+
# Logging Configuration
|
| 42 |
+
logging:
|
| 43 |
+
level: "INFO"
|
| 44 |
+
format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
| 45 |
+
|
| 46 |
+
# Hardware Requirements
|
| 47 |
+
hardware:
|
| 48 |
+
min_ram: "8GB"
|
| 49 |
+
recommended_ram: "16GB"
|
| 50 |
+
gpu_optional: true
|
| 51 |
+
cpu_optimized: true
|
deploy.py
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Deployment script for Petite Elle L'Aime 3 Gradio Application
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import os
|
| 7 |
+
import sys
|
| 8 |
+
import subprocess
|
| 9 |
+
import argparse
|
| 10 |
+
import yaml
|
| 11 |
+
from pathlib import Path
|
| 12 |
+
|
| 13 |
+
def load_config():
|
| 14 |
+
"""Load configuration from config.yaml"""
|
| 15 |
+
config_path = Path("config.yaml")
|
| 16 |
+
if config_path.exists():
|
| 17 |
+
with open(config_path, 'r') as f:
|
| 18 |
+
return yaml.safe_load(f)
|
| 19 |
+
return {}
|
| 20 |
+
|
| 21 |
+
def check_dependencies():
|
| 22 |
+
"""Check if all dependencies are installed"""
|
| 23 |
+
print("π Checking dependencies...")
|
| 24 |
+
|
| 25 |
+
required_packages = [
|
| 26 |
+
'gradio',
|
| 27 |
+
'torch',
|
| 28 |
+
'transformers',
|
| 29 |
+
'accelerate'
|
| 30 |
+
]
|
| 31 |
+
|
| 32 |
+
missing_packages = []
|
| 33 |
+
for package in required_packages:
|
| 34 |
+
try:
|
| 35 |
+
__import__(package)
|
| 36 |
+
print(f"β
{package}")
|
| 37 |
+
except ImportError:
|
| 38 |
+
missing_packages.append(package)
|
| 39 |
+
print(f"β {package}")
|
| 40 |
+
|
| 41 |
+
if missing_packages:
|
| 42 |
+
print(f"\nβ οΈ Missing packages: {', '.join(missing_packages)}")
|
| 43 |
+
print("Run: pip install -r requirements.txt")
|
| 44 |
+
return False
|
| 45 |
+
|
| 46 |
+
return True
|
| 47 |
+
|
| 48 |
+
def check_hardware():
|
| 49 |
+
"""Check hardware requirements"""
|
| 50 |
+
print("\nπ Checking hardware...")
|
| 51 |
+
|
| 52 |
+
import psutil
|
| 53 |
+
|
| 54 |
+
# Check RAM
|
| 55 |
+
ram_gb = psutil.virtual_memory().total / (1024**3)
|
| 56 |
+
print(f"RAM: {ram_gb:.1f} GB")
|
| 57 |
+
|
| 58 |
+
if ram_gb < 8:
|
| 59 |
+
print("β οΈ Warning: Less than 8GB RAM detected")
|
| 60 |
+
print(" The application may run slowly or fail to load the model")
|
| 61 |
+
else:
|
| 62 |
+
print("β
RAM requirements met")
|
| 63 |
+
|
| 64 |
+
# Check GPU
|
| 65 |
+
try:
|
| 66 |
+
import torch
|
| 67 |
+
if torch.cuda.is_available():
|
| 68 |
+
gpu_name = torch.cuda.get_device_name(0)
|
| 69 |
+
gpu_memory = torch.cuda.get_device_properties(0).total_memory / (1024**3)
|
| 70 |
+
print(f"GPU: {gpu_name} ({gpu_memory:.1f} GB)")
|
| 71 |
+
else:
|
| 72 |
+
print("GPU: Not available (will use CPU)")
|
| 73 |
+
except:
|
| 74 |
+
print("GPU: Unable to detect")
|
| 75 |
+
|
| 76 |
+
return True
|
| 77 |
+
|
| 78 |
+
def install_dependencies():
|
| 79 |
+
"""Install dependencies from requirements.txt"""
|
| 80 |
+
print("\nπ¦ Installing dependencies...")
|
| 81 |
+
|
| 82 |
+
if not os.path.exists("requirements.txt"):
|
| 83 |
+
print("β requirements.txt not found")
|
| 84 |
+
return False
|
| 85 |
+
|
| 86 |
+
try:
|
| 87 |
+
subprocess.run([sys.executable, "-m", "pip", "install", "-r", "requirements.txt"],
|
| 88 |
+
check=True, capture_output=True, text=True)
|
| 89 |
+
print("β
Dependencies installed successfully")
|
| 90 |
+
return True
|
| 91 |
+
except subprocess.CalledProcessError as e:
|
| 92 |
+
print(f"β Failed to install dependencies: {e}")
|
| 93 |
+
return False
|
| 94 |
+
|
| 95 |
+
def run_tests():
|
| 96 |
+
"""Run the test suite"""
|
| 97 |
+
print("\nπ§ͺ Running tests...")
|
| 98 |
+
|
| 99 |
+
if not os.path.exists("test_app.py"):
|
| 100 |
+
print("β test_app.py not found")
|
| 101 |
+
return False
|
| 102 |
+
|
| 103 |
+
try:
|
| 104 |
+
result = subprocess.run([sys.executable, "test_app.py"],
|
| 105 |
+
capture_output=True, text=True)
|
| 106 |
+
print(result.stdout)
|
| 107 |
+
if result.stderr:
|
| 108 |
+
print(result.stderr)
|
| 109 |
+
return result.returncode == 0
|
| 110 |
+
except Exception as e:
|
| 111 |
+
print(f"β Failed to run tests: {e}")
|
| 112 |
+
return False
|
| 113 |
+
|
| 114 |
+
def start_application(port=None, host=None):
|
| 115 |
+
"""Start the Gradio application"""
|
| 116 |
+
print("\nπ Starting application...")
|
| 117 |
+
|
| 118 |
+
config = load_config()
|
| 119 |
+
ui_config = config.get('ui', {})
|
| 120 |
+
|
| 121 |
+
# Use provided arguments or config defaults
|
| 122 |
+
port = port or ui_config.get('server_port', 7860)
|
| 123 |
+
host = host or ui_config.get('server_name', '0.0.0.0')
|
| 124 |
+
|
| 125 |
+
print(f"π Application will be available at: http://{host}:{port}")
|
| 126 |
+
print("π Press Ctrl+C to stop the application")
|
| 127 |
+
|
| 128 |
+
try:
|
| 129 |
+
subprocess.run([sys.executable, "app.py"], check=True)
|
| 130 |
+
except KeyboardInterrupt:
|
| 131 |
+
print("\nπ Application stopped by user")
|
| 132 |
+
except subprocess.CalledProcessError as e:
|
| 133 |
+
print(f"β Failed to start application: {e}")
|
| 134 |
+
return False
|
| 135 |
+
|
| 136 |
+
return True
|
| 137 |
+
|
| 138 |
+
def main():
|
| 139 |
+
"""Main deployment function"""
|
| 140 |
+
parser = argparse.ArgumentParser(description="Deploy Petite Elle L'Aime 3 Gradio Application")
|
| 141 |
+
parser.add_argument("--install", action="store_true", help="Install dependencies")
|
| 142 |
+
parser.add_argument("--test", action="store_true", help="Run tests")
|
| 143 |
+
parser.add_argument("--check", action="store_true", help="Check system requirements")
|
| 144 |
+
parser.add_argument("--port", type=int, help="Port to run the application on")
|
| 145 |
+
parser.add_argument("--host", type=str, help="Host to bind the application to")
|
| 146 |
+
parser.add_argument("--start", action="store_true", help="Start the application")
|
| 147 |
+
|
| 148 |
+
args = parser.parse_args()
|
| 149 |
+
|
| 150 |
+
print("π€ Petite Elle L'Aime 3 - Deployment Script\n")
|
| 151 |
+
|
| 152 |
+
# If no arguments provided, run full deployment
|
| 153 |
+
if not any([args.install, args.test, args.check, args.start]):
|
| 154 |
+
args.install = True
|
| 155 |
+
args.test = True
|
| 156 |
+
args.check = True
|
| 157 |
+
args.start = True
|
| 158 |
+
|
| 159 |
+
success = True
|
| 160 |
+
|
| 161 |
+
if args.install:
|
| 162 |
+
success &= install_dependencies()
|
| 163 |
+
|
| 164 |
+
if args.check:
|
| 165 |
+
success &= check_dependencies()
|
| 166 |
+
success &= check_hardware()
|
| 167 |
+
|
| 168 |
+
if args.test:
|
| 169 |
+
success &= run_tests()
|
| 170 |
+
|
| 171 |
+
if args.start and success:
|
| 172 |
+
start_application(args.port, args.host)
|
| 173 |
+
|
| 174 |
+
if not success:
|
| 175 |
+
print("\nβ Deployment failed. Please fix the issues above.")
|
| 176 |
+
sys.exit(1)
|
| 177 |
+
else:
|
| 178 |
+
print("\nβ
Deployment completed successfully!")
|
| 179 |
+
|
| 180 |
+
if __name__ == "__main__":
|
| 181 |
+
main()
|
download_model.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Helper script to download the int4 model files at build time for Hugging Face Spaces
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import os
|
| 7 |
+
import sys
|
| 8 |
+
import subprocess
|
| 9 |
+
import logging
|
| 10 |
+
from pathlib import Path
|
| 11 |
+
|
| 12 |
+
# Configure logging
|
| 13 |
+
logging.basicConfig(level=logging.INFO)
|
| 14 |
+
logger = logging.getLogger(__name__)
|
| 15 |
+
|
| 16 |
+
# Model configuration
|
| 17 |
+
MAIN_MODEL_ID = "Tonic/petite-elle-L-aime-3-sft"
|
| 18 |
+
INT4_MODEL_ID = "Tonic/petite-elle-L-aime-3-sft/int4"
|
| 19 |
+
LOCAL_MODEL_PATH = "./int4"
|
| 20 |
+
|
| 21 |
+
def download_model():
|
| 22 |
+
"""Download the int4 model files to local directory"""
|
| 23 |
+
try:
|
| 24 |
+
logger.info(f"Downloading int4 model from {INT4_MODEL_ID}")
|
| 25 |
+
|
| 26 |
+
# Create local directory if it doesn't exist
|
| 27 |
+
os.makedirs(LOCAL_MODEL_PATH, exist_ok=True)
|
| 28 |
+
|
| 29 |
+
# Use huggingface_hub to download the model
|
| 30 |
+
from huggingface_hub import snapshot_download
|
| 31 |
+
|
| 32 |
+
# Download the int4 model files
|
| 33 |
+
snapshot_download(
|
| 34 |
+
repo_id=INT4_MODEL_ID,
|
| 35 |
+
local_dir=LOCAL_MODEL_PATH,
|
| 36 |
+
local_dir_use_symlinks=False,
|
| 37 |
+
ignore_patterns=["*.md", "*.txt", "*.git*", "*.ipynb", "*.py"]
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
logger.info(f"Model downloaded successfully to {LOCAL_MODEL_PATH}")
|
| 41 |
+
return True
|
| 42 |
+
|
| 43 |
+
except Exception as e:
|
| 44 |
+
logger.error(f"Error downloading model: {e}")
|
| 45 |
+
return False
|
| 46 |
+
|
| 47 |
+
def check_model_files():
|
| 48 |
+
"""Check if required model files exist"""
|
| 49 |
+
required_files = [
|
| 50 |
+
"config.json",
|
| 51 |
+
"pytorch_model.bin",
|
| 52 |
+
"tokenizer.json",
|
| 53 |
+
"tokenizer_config.json"
|
| 54 |
+
]
|
| 55 |
+
|
| 56 |
+
missing_files = []
|
| 57 |
+
for file in required_files:
|
| 58 |
+
file_path = os.path.join(LOCAL_MODEL_PATH, file)
|
| 59 |
+
if not os.path.exists(file_path):
|
| 60 |
+
missing_files.append(file)
|
| 61 |
+
|
| 62 |
+
if missing_files:
|
| 63 |
+
logger.error(f"Missing model files: {missing_files}")
|
| 64 |
+
return False
|
| 65 |
+
|
| 66 |
+
logger.info("All required model files found")
|
| 67 |
+
return True
|
| 68 |
+
|
| 69 |
+
def verify_model_integrity():
|
| 70 |
+
"""Verify that the downloaded model files are valid"""
|
| 71 |
+
try:
|
| 72 |
+
# Try to load the tokenizer to verify it's working
|
| 73 |
+
from transformers import AutoTokenizer
|
| 74 |
+
tokenizer = AutoTokenizer.from_pretrained(LOCAL_MODEL_PATH)
|
| 75 |
+
logger.info("Tokenizer loaded successfully from local files")
|
| 76 |
+
|
| 77 |
+
# Try to load the model config
|
| 78 |
+
from transformers import AutoConfig
|
| 79 |
+
config = AutoConfig.from_pretrained(LOCAL_MODEL_PATH)
|
| 80 |
+
logger.info("Model config loaded successfully from local files")
|
| 81 |
+
|
| 82 |
+
return True
|
| 83 |
+
|
| 84 |
+
except Exception as e:
|
| 85 |
+
logger.error(f"Error verifying model integrity: {e}")
|
| 86 |
+
return False
|
| 87 |
+
|
| 88 |
+
def main():
|
| 89 |
+
"""Main function to download model at build time"""
|
| 90 |
+
logger.info("Starting model download for Hugging Face Space...")
|
| 91 |
+
|
| 92 |
+
# Check if model files already exist
|
| 93 |
+
if check_model_files():
|
| 94 |
+
logger.info("Model files already exist, verifying integrity...")
|
| 95 |
+
if verify_model_integrity():
|
| 96 |
+
logger.info("Model files verified successfully")
|
| 97 |
+
return True
|
| 98 |
+
else:
|
| 99 |
+
logger.warning("Model files exist but failed integrity check, re-downloading...")
|
| 100 |
+
|
| 101 |
+
# Download the model
|
| 102 |
+
if download_model():
|
| 103 |
+
logger.info("Model download completed successfully")
|
| 104 |
+
|
| 105 |
+
# Verify the downloaded files
|
| 106 |
+
if check_model_files() and verify_model_integrity():
|
| 107 |
+
logger.info("Model download and verification completed successfully")
|
| 108 |
+
return True
|
| 109 |
+
else:
|
| 110 |
+
logger.error("Model download completed but verification failed")
|
| 111 |
+
return False
|
| 112 |
+
else:
|
| 113 |
+
logger.error("Model download failed")
|
| 114 |
+
return False
|
| 115 |
+
|
| 116 |
+
if __name__ == "__main__":
|
| 117 |
+
success = main()
|
| 118 |
+
sys.exit(0 if success else 1)
|
download_model_advanced.py
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Advanced helper script to download the int4 model files using HfFileSystem
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import os
|
| 7 |
+
import sys
|
| 8 |
+
import logging
|
| 9 |
+
from pathlib import Path
|
| 10 |
+
from tqdm import tqdm
|
| 11 |
+
|
| 12 |
+
# Configure logging
|
| 13 |
+
logging.basicConfig(level=logging.INFO)
|
| 14 |
+
logger = logging.getLogger(__name__)
|
| 15 |
+
|
| 16 |
+
# Model configuration
|
| 17 |
+
MAIN_MODEL_ID = "Tonic/petite-elle-L-aime-3-sft"
|
| 18 |
+
INT4_MODEL_ID = "Tonic/petite-elle-L-aime-3-sft/int4"
|
| 19 |
+
LOCAL_MODEL_PATH = "./int4"
|
| 20 |
+
|
| 21 |
+
def get_file_info(fs, repo_path):
|
| 22 |
+
"""Get detailed information about files in the repository"""
|
| 23 |
+
try:
|
| 24 |
+
files = fs.ls(repo_path, detail=True)
|
| 25 |
+
return [f for f in files if f['type'] == 'file']
|
| 26 |
+
except Exception as e:
|
| 27 |
+
logger.error(f"Error listing files in {repo_path}: {e}")
|
| 28 |
+
return []
|
| 29 |
+
|
| 30 |
+
def download_with_progress(fs, remote_path, local_path, file_size):
|
| 31 |
+
"""Download a file with progress bar"""
|
| 32 |
+
try:
|
| 33 |
+
# Create directory if it doesn't exist
|
| 34 |
+
os.makedirs(os.path.dirname(local_path), exist_ok=True)
|
| 35 |
+
|
| 36 |
+
# Download with progress bar
|
| 37 |
+
with tqdm(total=file_size, unit='B', unit_scale=True, desc=os.path.basename(local_path)) as pbar:
|
| 38 |
+
with fs.open(remote_path, 'rb') as remote_file:
|
| 39 |
+
with open(local_path, 'wb') as local_file:
|
| 40 |
+
chunk_size = 8192
|
| 41 |
+
while True:
|
| 42 |
+
chunk = remote_file.read(chunk_size)
|
| 43 |
+
if not chunk:
|
| 44 |
+
break
|
| 45 |
+
local_file.write(chunk)
|
| 46 |
+
pbar.update(len(chunk))
|
| 47 |
+
|
| 48 |
+
return True
|
| 49 |
+
except Exception as e:
|
| 50 |
+
logger.error(f"Error downloading {remote_path}: {e}")
|
| 51 |
+
return False
|
| 52 |
+
|
| 53 |
+
def download_model_advanced():
|
| 54 |
+
"""Download the int4 model files using advanced HfFileSystem features"""
|
| 55 |
+
try:
|
| 56 |
+
logger.info(f"Downloading int4 model from {INT4_MODEL_ID}")
|
| 57 |
+
|
| 58 |
+
# Create local directory if it doesn't exist
|
| 59 |
+
os.makedirs(LOCAL_MODEL_PATH, exist_ok=True)
|
| 60 |
+
|
| 61 |
+
# Use HfFileSystem for downloading
|
| 62 |
+
from huggingface_hub import HfFileSystem
|
| 63 |
+
|
| 64 |
+
# Initialize the file system
|
| 65 |
+
fs = HfFileSystem()
|
| 66 |
+
|
| 67 |
+
# Check if repository exists
|
| 68 |
+
if not fs.exists(INT4_MODEL_ID):
|
| 69 |
+
logger.error(f"Repository {INT4_MODEL_ID} does not exist")
|
| 70 |
+
return False
|
| 71 |
+
|
| 72 |
+
# Get file information
|
| 73 |
+
files = get_file_info(fs, INT4_MODEL_ID)
|
| 74 |
+
if not files:
|
| 75 |
+
logger.error("No files found in repository")
|
| 76 |
+
return False
|
| 77 |
+
|
| 78 |
+
# Filter essential model files
|
| 79 |
+
essential_files = [
|
| 80 |
+
'config.json',
|
| 81 |
+
'pytorch_model.bin',
|
| 82 |
+
'tokenizer.json',
|
| 83 |
+
'tokenizer_config.json',
|
| 84 |
+
'special_tokens_map.json',
|
| 85 |
+
'generation_config.json'
|
| 86 |
+
]
|
| 87 |
+
|
| 88 |
+
files_to_download = []
|
| 89 |
+
for file_info in files:
|
| 90 |
+
file_name = os.path.basename(file_info['name'])
|
| 91 |
+
if file_name in essential_files:
|
| 92 |
+
files_to_download.append(file_info)
|
| 93 |
+
|
| 94 |
+
logger.info(f"Found {len(files_to_download)} essential files to download")
|
| 95 |
+
|
| 96 |
+
# Download each file
|
| 97 |
+
successful_downloads = 0
|
| 98 |
+
for file_info in files_to_download:
|
| 99 |
+
file_path = file_info['name']
|
| 100 |
+
file_name = os.path.basename(file_path)
|
| 101 |
+
local_file_path = os.path.join(LOCAL_MODEL_PATH, file_name)
|
| 102 |
+
file_size = file_info.get('size', 0)
|
| 103 |
+
|
| 104 |
+
logger.info(f"Downloading {file_name} ({file_size} bytes)...")
|
| 105 |
+
|
| 106 |
+
# Download the file with progress
|
| 107 |
+
if download_with_progress(fs, file_path, local_file_path, file_size):
|
| 108 |
+
successful_downloads += 1
|
| 109 |
+
logger.info(f"Successfully downloaded {file_name}")
|
| 110 |
+
else:
|
| 111 |
+
logger.error(f"Failed to download {file_name}")
|
| 112 |
+
|
| 113 |
+
logger.info(f"Downloaded {successful_downloads}/{len(files_to_download)} files")
|
| 114 |
+
return successful_downloads == len(files_to_download)
|
| 115 |
+
|
| 116 |
+
except Exception as e:
|
| 117 |
+
logger.error(f"Error downloading model: {e}")
|
| 118 |
+
return False
|
| 119 |
+
|
| 120 |
+
def verify_download_advanced():
|
| 121 |
+
"""Advanced verification of downloaded model files"""
|
| 122 |
+
try:
|
| 123 |
+
logger.info("Verifying downloaded model files...")
|
| 124 |
+
|
| 125 |
+
# Expected file sizes (approximate)
|
| 126 |
+
expected_files = {
|
| 127 |
+
"config.json": (1000, 10000), # (min_size, max_size) in bytes
|
| 128 |
+
"pytorch_model.bin": (1000000, 5000000000), # Should be several MB
|
| 129 |
+
"tokenizer.json": (10000, 1000000), # Should be several KB
|
| 130 |
+
"tokenizer_config.json": (100, 10000), # Minimum size
|
| 131 |
+
"special_tokens_map.json": (100, 10000),
|
| 132 |
+
"generation_config.json": (100, 10000)
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
verification_results = []
|
| 136 |
+
|
| 137 |
+
for file_name, (min_size, max_size) in expected_files.items():
|
| 138 |
+
file_path = os.path.join(LOCAL_MODEL_PATH, file_name)
|
| 139 |
+
if os.path.exists(file_path):
|
| 140 |
+
actual_size = os.path.getsize(file_path)
|
| 141 |
+
if min_size <= actual_size <= max_size:
|
| 142 |
+
logger.info(f"β
{file_name} verified ({actual_size} bytes)")
|
| 143 |
+
verification_results.append(True)
|
| 144 |
+
else:
|
| 145 |
+
logger.warning(f"β οΈ {file_name} size unexpected ({actual_size} bytes)")
|
| 146 |
+
verification_results.append(False)
|
| 147 |
+
else:
|
| 148 |
+
logger.error(f"β Missing {file_name}")
|
| 149 |
+
verification_results.append(False)
|
| 150 |
+
|
| 151 |
+
success_rate = sum(verification_results) / len(verification_results)
|
| 152 |
+
logger.info(f"Verification complete: {sum(verification_results)}/{len(verification_results)} files valid")
|
| 153 |
+
|
| 154 |
+
return success_rate >= 0.8 # Allow 20% tolerance
|
| 155 |
+
|
| 156 |
+
except Exception as e:
|
| 157 |
+
logger.error(f"Error verifying files: {e}")
|
| 158 |
+
return False
|
| 159 |
+
|
| 160 |
+
def check_model_files():
|
| 161 |
+
"""Check if required model files exist"""
|
| 162 |
+
required_files = [
|
| 163 |
+
"config.json",
|
| 164 |
+
"pytorch_model.bin",
|
| 165 |
+
"tokenizer.json",
|
| 166 |
+
"tokenizer_config.json"
|
| 167 |
+
]
|
| 168 |
+
|
| 169 |
+
missing_files = []
|
| 170 |
+
for file in required_files:
|
| 171 |
+
file_path = os.path.join(LOCAL_MODEL_PATH, file)
|
| 172 |
+
if not os.path.exists(file_path):
|
| 173 |
+
missing_files.append(file)
|
| 174 |
+
|
| 175 |
+
if missing_files:
|
| 176 |
+
logger.error(f"Missing model files: {missing_files}")
|
| 177 |
+
return False
|
| 178 |
+
|
| 179 |
+
logger.info("All required model files found")
|
| 180 |
+
return True
|
| 181 |
+
|
| 182 |
+
def main():
|
| 183 |
+
"""Main function to download model at build time"""
|
| 184 |
+
logger.info("Starting advanced model download for Hugging Face Space...")
|
| 185 |
+
|
| 186 |
+
# Check if model files already exist
|
| 187 |
+
if check_model_files():
|
| 188 |
+
logger.info("Model files already exist, skipping download")
|
| 189 |
+
return True
|
| 190 |
+
|
| 191 |
+
# Download the model using advanced method
|
| 192 |
+
if download_model_advanced():
|
| 193 |
+
# Verify the download
|
| 194 |
+
if verify_download_advanced():
|
| 195 |
+
logger.info("Model download and verification completed successfully")
|
| 196 |
+
return True
|
| 197 |
+
else:
|
| 198 |
+
logger.error("Model verification failed")
|
| 199 |
+
return False
|
| 200 |
+
else:
|
| 201 |
+
logger.error("Model download failed")
|
| 202 |
+
return False
|
| 203 |
+
|
| 204 |
+
if __name__ == "__main__":
|
| 205 |
+
success = main()
|
| 206 |
+
sys.exit(0 if success else 1)
|
requirements.txt
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio>=5.38.2
|
| 2 |
+
torch>=2.0.0
|
| 3 |
+
transformers>=4.54.0
|
| 4 |
+
accelerate>=0.20.0
|
| 5 |
+
torchao>=0.1.0
|
| 6 |
+
safetensors>=0.4.0
|
| 7 |
+
tokenizers>=0.21.2
|
| 8 |
+
pyyaml>=6.0
|
| 9 |
+
psutil>=5.9.0
|
| 10 |
+
huggingface_hub>=0.20.0
|
| 11 |
+
tqdm>=4.64.0
|
test_app.py
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Test script for the Petite Elle L'Aime 3 Gradio application
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import sys
|
| 7 |
+
import os
|
| 8 |
+
import importlib.util
|
| 9 |
+
|
| 10 |
+
def test_imports():
|
| 11 |
+
"""Test if all required packages can be imported"""
|
| 12 |
+
required_packages = [
|
| 13 |
+
'gradio',
|
| 14 |
+
'torch',
|
| 15 |
+
'transformers',
|
| 16 |
+
'accelerate',
|
| 17 |
+
'safetensors',
|
| 18 |
+
'tokenizers'
|
| 19 |
+
]
|
| 20 |
+
|
| 21 |
+
print("Testing imports...")
|
| 22 |
+
for package in required_packages:
|
| 23 |
+
try:
|
| 24 |
+
__import__(package)
|
| 25 |
+
print(f"β
{package} imported successfully")
|
| 26 |
+
except ImportError as e:
|
| 27 |
+
print(f"β Failed to import {package}: {e}")
|
| 28 |
+
return False
|
| 29 |
+
|
| 30 |
+
return True
|
| 31 |
+
|
| 32 |
+
def test_app_structure():
|
| 33 |
+
"""Test if the app.py file has the correct structure"""
|
| 34 |
+
print("\nTesting app.py structure...")
|
| 35 |
+
|
| 36 |
+
if not os.path.exists('app.py'):
|
| 37 |
+
print("β app.py not found")
|
| 38 |
+
return False
|
| 39 |
+
|
| 40 |
+
try:
|
| 41 |
+
# Import the app module
|
| 42 |
+
spec = importlib.util.spec_from_file_location("app", "app.py")
|
| 43 |
+
app_module = importlib.util.module_from_spec(spec)
|
| 44 |
+
spec.loader.exec_module(app_module)
|
| 45 |
+
|
| 46 |
+
# Check for required functions
|
| 47 |
+
required_functions = [
|
| 48 |
+
'load_model',
|
| 49 |
+
'create_prompt',
|
| 50 |
+
'generate_response',
|
| 51 |
+
'user',
|
| 52 |
+
'bot'
|
| 53 |
+
]
|
| 54 |
+
|
| 55 |
+
for func_name in required_functions:
|
| 56 |
+
if hasattr(app_module, func_name):
|
| 57 |
+
print(f"β
{func_name} function found")
|
| 58 |
+
else:
|
| 59 |
+
print(f"β {func_name} function not found")
|
| 60 |
+
return False
|
| 61 |
+
|
| 62 |
+
# Check for required variables
|
| 63 |
+
required_variables = [
|
| 64 |
+
'DEFAULT_SYSTEM_PROMPT',
|
| 65 |
+
'title',
|
| 66 |
+
'description',
|
| 67 |
+
'presentation1',
|
| 68 |
+
'presentation2',
|
| 69 |
+
'joinus'
|
| 70 |
+
]
|
| 71 |
+
|
| 72 |
+
for var_name in required_variables:
|
| 73 |
+
if hasattr(app_module, var_name):
|
| 74 |
+
print(f"β
{var_name} variable found")
|
| 75 |
+
else:
|
| 76 |
+
print(f"β {var_name} variable not found")
|
| 77 |
+
return False
|
| 78 |
+
|
| 79 |
+
print("β
All required functions and variables found")
|
| 80 |
+
return True
|
| 81 |
+
|
| 82 |
+
except Exception as e:
|
| 83 |
+
print(f"β Error testing app.py: {e}")
|
| 84 |
+
return False
|
| 85 |
+
|
| 86 |
+
def test_requirements():
|
| 87 |
+
"""Test if requirements.txt exists and has required packages"""
|
| 88 |
+
print("\nTesting requirements.txt...")
|
| 89 |
+
|
| 90 |
+
if not os.path.exists('requirements.txt'):
|
| 91 |
+
print("β requirements.txt not found")
|
| 92 |
+
return False
|
| 93 |
+
|
| 94 |
+
required_packages = [
|
| 95 |
+
'gradio',
|
| 96 |
+
'torch',
|
| 97 |
+
'transformers',
|
| 98 |
+
'accelerate'
|
| 99 |
+
]
|
| 100 |
+
|
| 101 |
+
with open('requirements.txt', 'r') as f:
|
| 102 |
+
content = f.read()
|
| 103 |
+
|
| 104 |
+
for package in required_packages:
|
| 105 |
+
if package in content:
|
| 106 |
+
print(f"β
{package} found in requirements.txt")
|
| 107 |
+
else:
|
| 108 |
+
print(f"β {package} not found in requirements.txt")
|
| 109 |
+
return False
|
| 110 |
+
|
| 111 |
+
return True
|
| 112 |
+
|
| 113 |
+
def test_config():
|
| 114 |
+
"""Test if config.yaml exists and has required sections"""
|
| 115 |
+
print("\nTesting config.yaml...")
|
| 116 |
+
|
| 117 |
+
if not os.path.exists('config.yaml'):
|
| 118 |
+
print("β config.yaml not found")
|
| 119 |
+
return False
|
| 120 |
+
|
| 121 |
+
try:
|
| 122 |
+
import yaml
|
| 123 |
+
with open('config.yaml', 'r') as f:
|
| 124 |
+
config = yaml.safe_load(f)
|
| 125 |
+
|
| 126 |
+
required_sections = [
|
| 127 |
+
'model',
|
| 128 |
+
'system_prompt',
|
| 129 |
+
'generation',
|
| 130 |
+
'chat',
|
| 131 |
+
'ui'
|
| 132 |
+
]
|
| 133 |
+
|
| 134 |
+
for section in required_sections:
|
| 135 |
+
if section in config:
|
| 136 |
+
print(f"β
{section} section found in config.yaml")
|
| 137 |
+
else:
|
| 138 |
+
print(f"β {section} section not found in config.yaml")
|
| 139 |
+
return False
|
| 140 |
+
|
| 141 |
+
# Check system prompt default
|
| 142 |
+
if 'system_prompt' in config and 'default' in config['system_prompt']:
|
| 143 |
+
print(f"β
System prompt default found: {config['system_prompt']['default']}")
|
| 144 |
+
else:
|
| 145 |
+
print("β System prompt default not found")
|
| 146 |
+
return False
|
| 147 |
+
|
| 148 |
+
return True
|
| 149 |
+
|
| 150 |
+
except Exception as e:
|
| 151 |
+
print(f"β Error testing config.yaml: {e}")
|
| 152 |
+
return False
|
| 153 |
+
|
| 154 |
+
def test_readme():
|
| 155 |
+
"""Test if README.md has the correct structure"""
|
| 156 |
+
print("\nTesting README.md...")
|
| 157 |
+
|
| 158 |
+
if not os.path.exists('README.md'):
|
| 159 |
+
print("β README.md not found")
|
| 160 |
+
return False
|
| 161 |
+
|
| 162 |
+
with open('README.md', 'r') as f:
|
| 163 |
+
content = f.read()
|
| 164 |
+
|
| 165 |
+
required_sections = [
|
| 166 |
+
'Petite Elle L\'Aime 3',
|
| 167 |
+
'Features',
|
| 168 |
+
'Installation',
|
| 169 |
+
'Usage'
|
| 170 |
+
]
|
| 171 |
+
|
| 172 |
+
for section in required_sections:
|
| 173 |
+
if section in content:
|
| 174 |
+
print(f"β
{section} section found")
|
| 175 |
+
else:
|
| 176 |
+
print(f"β {section} section not found")
|
| 177 |
+
return False
|
| 178 |
+
|
| 179 |
+
return True
|
| 180 |
+
|
| 181 |
+
def main():
|
| 182 |
+
"""Run all tests"""
|
| 183 |
+
print("π§ͺ Testing Petite Elle L'Aime 3 Gradio Application\n")
|
| 184 |
+
|
| 185 |
+
tests = [
|
| 186 |
+
test_imports,
|
| 187 |
+
test_app_structure,
|
| 188 |
+
test_requirements,
|
| 189 |
+
test_config,
|
| 190 |
+
test_readme
|
| 191 |
+
]
|
| 192 |
+
|
| 193 |
+
passed = 0
|
| 194 |
+
total = len(tests)
|
| 195 |
+
|
| 196 |
+
for test in tests:
|
| 197 |
+
if test():
|
| 198 |
+
passed += 1
|
| 199 |
+
print()
|
| 200 |
+
|
| 201 |
+
print(f"π Test Results: {passed}/{total} tests passed")
|
| 202 |
+
|
| 203 |
+
if passed == total:
|
| 204 |
+
print("π All tests passed! The application is ready to run.")
|
| 205 |
+
print("\nTo run the application:")
|
| 206 |
+
print("python app.py")
|
| 207 |
+
else:
|
| 208 |
+
print("β Some tests failed. Please fix the issues before running the application.")
|
| 209 |
+
sys.exit(1)
|
| 210 |
+
|
| 211 |
+
if __name__ == "__main__":
|
| 212 |
+
main()
|
test_model_loading.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Test script to verify model loading functionality
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import os
|
| 7 |
+
import sys
|
| 8 |
+
import logging
|
| 9 |
+
|
| 10 |
+
# Configure logging
|
| 11 |
+
logging.basicConfig(level=logging.INFO)
|
| 12 |
+
logger = logging.getLogger(__name__)
|
| 13 |
+
|
| 14 |
+
def test_model_loading():
|
| 15 |
+
"""Test the model loading functionality"""
|
| 16 |
+
try:
|
| 17 |
+
logger.info("Testing model loading...")
|
| 18 |
+
|
| 19 |
+
# Import the app module to test model loading
|
| 20 |
+
from app import load_model, check_local_model
|
| 21 |
+
|
| 22 |
+
# Check if local model exists
|
| 23 |
+
has_local = check_local_model()
|
| 24 |
+
logger.info(f"Local model available: {has_local}")
|
| 25 |
+
|
| 26 |
+
# Try to load the model
|
| 27 |
+
success = load_model()
|
| 28 |
+
logger.info(f"Model loading successful: {success}")
|
| 29 |
+
|
| 30 |
+
return success
|
| 31 |
+
|
| 32 |
+
except Exception as e:
|
| 33 |
+
logger.error(f"Error testing model loading: {e}")
|
| 34 |
+
return False
|
| 35 |
+
|
| 36 |
+
def test_download_script():
|
| 37 |
+
"""Test the download script"""
|
| 38 |
+
try:
|
| 39 |
+
logger.info("Testing download script...")
|
| 40 |
+
|
| 41 |
+
# Import and run the download script
|
| 42 |
+
from download_model import main as download_main
|
| 43 |
+
|
| 44 |
+
success = download_main()
|
| 45 |
+
logger.info(f"Download script successful: {success}")
|
| 46 |
+
|
| 47 |
+
return success
|
| 48 |
+
|
| 49 |
+
except Exception as e:
|
| 50 |
+
logger.error(f"Error testing download script: {e}")
|
| 51 |
+
return False
|
| 52 |
+
|
| 53 |
+
def main():
|
| 54 |
+
"""Main test function"""
|
| 55 |
+
logger.info("Starting model loading tests...")
|
| 56 |
+
|
| 57 |
+
# Test download script first
|
| 58 |
+
logger.info("=== Testing Download Script ===")
|
| 59 |
+
download_success = test_download_script()
|
| 60 |
+
|
| 61 |
+
# Test model loading
|
| 62 |
+
logger.info("=== Testing Model Loading ===")
|
| 63 |
+
loading_success = test_model_loading()
|
| 64 |
+
|
| 65 |
+
# Summary
|
| 66 |
+
logger.info("=== Test Summary ===")
|
| 67 |
+
logger.info(f"Download script: {'PASS' if download_success else 'FAIL'}")
|
| 68 |
+
logger.info(f"Model loading: {'PASS' if loading_success else 'FAIL'}")
|
| 69 |
+
|
| 70 |
+
overall_success = download_success and loading_success
|
| 71 |
+
|
| 72 |
+
if overall_success:
|
| 73 |
+
logger.info("All tests passed! Model is ready for deployment.")
|
| 74 |
+
else:
|
| 75 |
+
logger.error("Some tests failed. Please check the logs above.")
|
| 76 |
+
|
| 77 |
+
return overall_success
|
| 78 |
+
|
| 79 |
+
if __name__ == "__main__":
|
| 80 |
+
success = main()
|
| 81 |
+
sys.exit(0 if success else 1)
|