Tonic commited on
Commit
19b19f0
Β·
1 Parent(s): 6f9d970

tries to download the model at build time

Browse files
Files changed (11) hide show
  1. .gitignore +82 -0
  2. README.md +178 -1
  3. app.py +303 -0
  4. build.py +113 -0
  5. config.yaml +51 -0
  6. deploy.py +181 -0
  7. download_model.py +118 -0
  8. download_model_advanced.py +206 -0
  9. requirements.txt +11 -0
  10. test_app.py +212 -0
  11. 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)