harismlnaslm commited on
Commit
f67dde9
·
1 Parent(s): 119d2a6

Simplify app.py and requirements.txt for HF Spaces compatibility

Browse files
Files changed (2) hide show
  1. app.py +45 -256
  2. requirements.txt +15 -24
app.py CHANGED
@@ -1,7 +1,7 @@
1
  #!/usr/bin/env python3
2
  """
3
  Textilindo AI Assistant - Hugging Face Spaces FastAPI Application
4
- Main application file for deployment on Hugging Face Spaces
5
  """
6
 
7
  import os
@@ -12,21 +12,11 @@ from datetime import datetime
12
  from typing import Optional, Dict, Any
13
  from fastapi import FastAPI, HTTPException, Request, BackgroundTasks
14
  from fastapi.responses import HTMLResponse, JSONResponse
15
- from fastapi.staticfiles import StaticFiles
16
  from fastapi.middleware.cors import CORSMiddleware
17
  from pydantic import BaseModel
18
  import uvicorn
19
- from huggingface_hub import InferenceClient
20
  import requests
21
 
22
- # Import torch only when needed for training
23
- try:
24
- import torch
25
- TORCH_AVAILABLE = True
26
- except ImportError:
27
- TORCH_AVAILABLE = False
28
- torch = None
29
-
30
  # Setup logging
31
  logging.basicConfig(level=logging.INFO)
32
  logger = logging.getLogger(__name__)
@@ -62,7 +52,6 @@ class HealthResponse(BaseModel):
62
  message: str
63
  version: str = "1.0.0"
64
 
65
- # Training models
66
  class TrainingRequest(BaseModel):
67
  model_name: str = "distilgpt2"
68
  dataset_path: str = "data/lora_dataset_20250910_145055.jsonl"
@@ -104,6 +93,7 @@ class TextilindoAI:
104
  self.client = None
105
  else:
106
  try:
 
107
  self.client = InferenceClient(
108
  token=self.api_key,
109
  model=self.model
@@ -200,165 +190,6 @@ Minimum purchase is 1 roll (67-70 yards)."""
200
  # Initialize AI assistant
201
  ai_assistant = TextilindoAI()
202
 
203
- # Training functions
204
- def load_training_data(dataset_path: str, max_samples: int = 20) -> list:
205
- """Load training data from JSONL file"""
206
- data = []
207
- try:
208
- with open(dataset_path, 'r', encoding='utf-8') as f:
209
- for i, line in enumerate(f):
210
- if i >= max_samples:
211
- break
212
- if line.strip():
213
- item = json.loads(line)
214
- # Create training text
215
- instruction = item.get('instruction', '')
216
- output = item.get('output', '')
217
- text = f"Question: {instruction} Answer: {output}"
218
- data.append({"text": text})
219
- logger.info(f"Loaded {len(data)} training samples")
220
- return data
221
- except Exception as e:
222
- logger.error(f"Error loading training data: {e}")
223
- return []
224
-
225
- async def train_model_async(
226
- model_name: str,
227
- dataset_path: str,
228
- config_path: str,
229
- max_samples: int,
230
- epochs: int,
231
- batch_size: int,
232
- learning_rate: float
233
- ):
234
- """Async training function"""
235
- global training_status
236
-
237
- try:
238
- training_status.update({
239
- "is_training": True,
240
- "status": "starting",
241
- "progress": 0,
242
- "start_time": datetime.now().isoformat(),
243
- "error": None
244
- })
245
-
246
- logger.info("🚀 Starting training...")
247
-
248
- # Import training libraries
249
- from transformers import (
250
- AutoTokenizer,
251
- AutoModelForCausalLM,
252
- TrainingArguments,
253
- Trainer,
254
- DataCollatorForLanguageModeling
255
- )
256
- from datasets import Dataset
257
-
258
- # Check GPU
259
- if not TORCH_AVAILABLE:
260
- raise Exception("PyTorch is required for training but not available")
261
-
262
- gpu_available = torch.cuda.is_available()
263
- logger.info(f"GPU available: {gpu_available}")
264
-
265
- # Load model and tokenizer
266
- logger.info(f"📥 Loading model: {model_name}")
267
- tokenizer = AutoTokenizer.from_pretrained(model_name)
268
- if tokenizer.pad_token is None:
269
- tokenizer.pad_token = tokenizer.eos_token
270
-
271
- # Load model
272
- if gpu_available:
273
- model = AutoModelForCausalLM.from_pretrained(
274
- model_name,
275
- torch_dtype=torch.float16,
276
- device_map="auto"
277
- )
278
- else:
279
- model = AutoModelForCausalLM.from_pretrained(model_name)
280
-
281
- logger.info("✅ Model loaded successfully")
282
-
283
- # Load training data
284
- training_data = load_training_data(dataset_path, max_samples)
285
- if not training_data:
286
- raise Exception("No training data loaded")
287
-
288
- # Convert to dataset
289
- dataset = Dataset.from_list(training_data)
290
-
291
- def tokenize_function(examples):
292
- return tokenizer(
293
- examples["text"],
294
- truncation=True,
295
- padding=True,
296
- max_length=256,
297
- return_tensors="pt"
298
- )
299
-
300
- tokenized_dataset = dataset.map(tokenize_function, batched=True)
301
-
302
- # Training arguments
303
- training_args = TrainingArguments(
304
- output_dir="./models/textilindo-trained",
305
- num_train_epochs=epochs,
306
- per_device_train_batch_size=batch_size,
307
- gradient_accumulation_steps=2,
308
- learning_rate=learning_rate,
309
- warmup_steps=5,
310
- save_steps=10,
311
- logging_steps=1,
312
- save_total_limit=1,
313
- prediction_loss_only=True,
314
- remove_unused_columns=False,
315
- fp16=gpu_available,
316
- dataloader_pin_memory=gpu_available,
317
- report_to=None,
318
- )
319
-
320
- # Data collator
321
- data_collator = DataCollatorForLanguageModeling(
322
- tokenizer=tokenizer,
323
- mlm=False,
324
- )
325
-
326
- # Create trainer
327
- trainer = Trainer(
328
- model=model,
329
- args=training_args,
330
- train_dataset=tokenized_dataset,
331
- data_collator=data_collator,
332
- tokenizer=tokenizer,
333
- )
334
-
335
- # Start training
336
- training_status["status"] = "training"
337
- trainer.train()
338
-
339
- # Save model
340
- model.save_pretrained("./models/textilindo-trained")
341
- tokenizer.save_pretrained("./models/textilindo-trained")
342
-
343
- # Update status
344
- training_status.update({
345
- "is_training": False,
346
- "status": "completed",
347
- "progress": 100,
348
- "end_time": datetime.now().isoformat()
349
- })
350
-
351
- logger.info("✅ Training completed successfully!")
352
-
353
- except Exception as e:
354
- logger.error(f"Training failed: {e}")
355
- training_status.update({
356
- "is_training": False,
357
- "status": "failed",
358
- "error": str(e),
359
- "end_time": datetime.now().isoformat()
360
- })
361
-
362
  # Routes
363
  @app.get("/", response_class=HTMLResponse)
364
  async def root():
@@ -482,43 +313,50 @@ async def get_info():
482
  }
483
  }
484
 
485
- # Training API endpoints
486
  @app.post("/api/train/start", response_model=TrainingResponse)
487
  async def start_training(request: TrainingRequest, background_tasks: BackgroundTasks):
488
- """Start training process"""
489
  global training_status
490
 
491
  if training_status["is_training"]:
492
  raise HTTPException(status_code=400, detail="Training already in progress")
493
 
494
- # Validate inputs
495
- if not Path(request.dataset_path).exists():
496
- raise HTTPException(status_code=404, detail=f"Dataset not found: {request.dataset_path}")
497
-
498
- if not Path(request.config_path).exists():
499
- raise HTTPException(status_code=404, detail=f"Config not found: {request.config_path}")
500
-
501
- # Start training in background
502
  training_id = f"train_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
503
 
504
- background_tasks.add_task(
505
- train_model_async,
506
- request.model_name,
507
- request.dataset_path,
508
- request.config_path,
509
- request.max_samples,
510
- request.epochs,
511
- request.batch_size,
512
- request.learning_rate
513
- )
 
514
 
515
  return TrainingResponse(
516
  success=True,
517
- message="Training started successfully",
518
  training_id=training_id,
519
  status="started"
520
  )
521
 
 
 
 
 
 
 
 
 
 
 
 
 
 
522
  @app.get("/api/train/status")
523
  async def get_training_status():
524
  """Get current training status"""
@@ -556,71 +394,23 @@ async def get_training_data_info():
556
 
557
  @app.get("/api/train/gpu")
558
  async def get_gpu_info():
559
- """Get GPU information"""
560
- if not TORCH_AVAILABLE:
561
- return {"available": False, "error": "PyTorch not available"}
562
-
563
- try:
564
- gpu_available = torch.cuda.is_available()
565
- if gpu_available:
566
- gpu_count = torch.cuda.device_count()
567
- gpu_name = torch.cuda.get_device_name(0)
568
- gpu_memory = torch.cuda.get_device_properties(0).total_memory / (1024**3)
569
- return {
570
- "available": True,
571
- "count": gpu_count,
572
- "name": gpu_name,
573
- "memory_gb": round(gpu_memory, 2)
574
- }
575
- else:
576
- return {"available": False}
577
- except Exception as e:
578
- return {"error": str(e)}
579
 
580
  @app.post("/api/train/test")
581
  async def test_trained_model():
582
- """Test the trained model"""
583
- if not TORCH_AVAILABLE:
584
- return {"error": "PyTorch is required for model testing but not available"}
585
-
586
- model_path = "./models/textilindo-trained"
587
- if not Path(model_path).exists():
588
- return {"error": "No trained model found"}
589
-
590
- try:
591
- from transformers import AutoTokenizer, AutoModelForCausalLM
592
-
593
- tokenizer = AutoTokenizer.from_pretrained(model_path)
594
- model = AutoModelForCausalLM.from_pretrained(model_path)
595
-
596
- # Test prompt
597
- test_prompt = "Question: dimana lokasi textilindo? Answer:"
598
- inputs = tokenizer(test_prompt, return_tensors="pt")
599
-
600
- with torch.no_grad():
601
- outputs = model.generate(
602
- **inputs,
603
- max_length=inputs.input_ids.shape[1] + 30,
604
- temperature=0.7,
605
- do_sample=True,
606
- pad_token_id=tokenizer.eos_token_id
607
- )
608
-
609
- response = tokenizer.decode(outputs[0], skip_special_tokens=True)
610
-
611
- return {
612
- "success": True,
613
- "test_prompt": test_prompt,
614
- "response": response,
615
- "model_path": model_path
616
- }
617
-
618
- except Exception as e:
619
- return {"error": str(e)}
620
-
621
- # Mount static files if they exist
622
- if Path("static").exists():
623
- app.mount("/static", StaticFiles(directory="static"), name="static")
624
 
625
  if __name__ == "__main__":
626
  # Get port from environment variable (Hugging Face Spaces uses 7860)
@@ -632,5 +422,4 @@ if __name__ == "__main__":
632
  host="0.0.0.0",
633
  port=port,
634
  log_level="info"
635
- )
636
- # Updated Mon, Oct 27, 2025 9:53:55 AM
 
1
  #!/usr/bin/env python3
2
  """
3
  Textilindo AI Assistant - Hugging Face Spaces FastAPI Application
4
+ Simplified version for HF Spaces deployment
5
  """
6
 
7
  import os
 
12
  from typing import Optional, Dict, Any
13
  from fastapi import FastAPI, HTTPException, Request, BackgroundTasks
14
  from fastapi.responses import HTMLResponse, JSONResponse
 
15
  from fastapi.middleware.cors import CORSMiddleware
16
  from pydantic import BaseModel
17
  import uvicorn
 
18
  import requests
19
 
 
 
 
 
 
 
 
 
20
  # Setup logging
21
  logging.basicConfig(level=logging.INFO)
22
  logger = logging.getLogger(__name__)
 
52
  message: str
53
  version: str = "1.0.0"
54
 
 
55
  class TrainingRequest(BaseModel):
56
  model_name: str = "distilgpt2"
57
  dataset_path: str = "data/lora_dataset_20250910_145055.jsonl"
 
93
  self.client = None
94
  else:
95
  try:
96
+ from huggingface_hub import InferenceClient
97
  self.client = InferenceClient(
98
  token=self.api_key,
99
  model=self.model
 
190
  # Initialize AI assistant
191
  ai_assistant = TextilindoAI()
192
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  # Routes
194
  @app.get("/", response_class=HTMLResponse)
195
  async def root():
 
313
  }
314
  }
315
 
316
+ # Training API endpoints (simplified for HF Spaces)
317
  @app.post("/api/train/start", response_model=TrainingResponse)
318
  async def start_training(request: TrainingRequest, background_tasks: BackgroundTasks):
319
+ """Start training process (simplified for HF Spaces)"""
320
  global training_status
321
 
322
  if training_status["is_training"]:
323
  raise HTTPException(status_code=400, detail="Training already in progress")
324
 
325
+ # For HF Spaces, we'll simulate training
 
 
 
 
 
 
 
326
  training_id = f"train_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
327
 
328
+ # Update status to show training started
329
+ training_status.update({
330
+ "is_training": True,
331
+ "status": "started",
332
+ "progress": 0,
333
+ "start_time": datetime.now().isoformat(),
334
+ "error": None
335
+ })
336
+
337
+ # Simulate training completion after a delay
338
+ background_tasks.add_task(simulate_training_completion)
339
 
340
  return TrainingResponse(
341
  success=True,
342
+ message="Training started successfully (simulated for HF Spaces)",
343
  training_id=training_id,
344
  status="started"
345
  )
346
 
347
+ async def simulate_training_completion():
348
+ """Simulate training completion for HF Spaces"""
349
+ import asyncio
350
+ await asyncio.sleep(10) # Simulate 10 seconds of training
351
+
352
+ global training_status
353
+ training_status.update({
354
+ "is_training": False,
355
+ "status": "completed",
356
+ "progress": 100,
357
+ "end_time": datetime.now().isoformat()
358
+ })
359
+
360
  @app.get("/api/train/status")
361
  async def get_training_status():
362
  """Get current training status"""
 
394
 
395
  @app.get("/api/train/gpu")
396
  async def get_gpu_info():
397
+ """Get GPU information (simulated for HF Spaces)"""
398
+ return {
399
+ "available": False,
400
+ "message": "GPU not available in HF Spaces free tier",
401
+ "recommendation": "Use local training or upgrade to paid tier"
402
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
403
 
404
  @app.post("/api/train/test")
405
  async def test_trained_model():
406
+ """Test the trained model (simulated)"""
407
+ return {
408
+ "success": True,
409
+ "message": "Model testing simulated for HF Spaces",
410
+ "test_prompt": "dimana lokasi textilindo?",
411
+ "response": "Textilindo berkantor pusat di Jl. Raya Prancis No.39, Kosambi Tim., Kec. Kosambi, Kabupaten Tangerang, Banten 15213",
412
+ "note": "This is a simulated response for HF Spaces demo"
413
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
414
 
415
  if __name__ == "__main__":
416
  # Get port from environment variable (Hugging Face Spaces uses 7860)
 
422
  host="0.0.0.0",
423
  port=port,
424
  log_level="info"
425
+ )
 
requirements.txt CHANGED
@@ -1,30 +1,22 @@
1
  # Textilindo AI Assistant - Hugging Face Spaces Requirements
2
- # Optimized for Hugging Face Spaces deployment
3
 
4
- # Core ML libraries (lightweight versions)
5
- torch>=2.0.0,<2.2.0
6
- transformers>=4.35.0,<4.40.0
7
- accelerate>=0.24.0
8
- peft>=0.6.0
9
- datasets>=2.14.0
10
 
11
- # HuggingFace tools
12
- huggingface-hub>=0.17.0
13
- tokenizers>=0.14.0
14
-
15
- # Web framework (FastAPI for Hugging Face Spaces)
16
- fastapi>=0.104.0
17
- uvicorn[standard]>=0.24.0
18
- python-multipart>=0.0.6
19
 
20
  # Data processing
21
- numpy>=1.24.0,<1.26.0
22
- pandas>=2.0.0,<2.2.0
23
  pyyaml>=6.0
24
 
25
  # HTTP requests
26
  requests>=2.31.0
27
- httpx>=0.25.0
28
 
29
  # Environment and configuration
30
  python-dotenv>=1.0.0
@@ -33,9 +25,8 @@ python-dotenv>=1.0.0
33
  tqdm>=4.65.0
34
  pydantic>=2.0.0
35
 
36
- # Optional: For better performance (if space allows)
37
- bitsandbytes>=0.41.0
38
- scipy>=1.10.0
39
-
40
- # Development and testing (minimal)
41
- pytest>=7.4.0
 
1
  # Textilindo AI Assistant - Hugging Face Spaces Requirements
2
+ # Lightweight version for HF Spaces
3
 
4
+ # Core web framework
5
+ fastapi==0.104.1
6
+ uvicorn[standard]==0.24.0
7
+ python-multipart==0.0.6
 
 
8
 
9
+ # HuggingFace tools (lightweight)
10
+ huggingface-hub==0.19.4
11
+ transformers==4.35.2
 
 
 
 
 
12
 
13
  # Data processing
14
+ numpy>=1.24.0
15
+ pandas>=2.0.0
16
  pyyaml>=6.0
17
 
18
  # HTTP requests
19
  requests>=2.31.0
 
20
 
21
  # Environment and configuration
22
  python-dotenv>=1.0.0
 
25
  tqdm>=4.65.0
26
  pydantic>=2.0.0
27
 
28
+ # Optional ML libraries (only if needed)
29
+ # torch>=2.0.0
30
+ # accelerate>=0.24.0
31
+ # peft>=0.6.0
32
+ # datasets>=2.14.0