AdityaAjithKumar commited on
Commit
af67663
·
1 Parent(s): 47bedb5

python-multipart + logging + /health endpoint

Browse files
Files changed (3) hide show
  1. Dockerfile +3 -3
  2. app.py +54 -16
  3. requirements.txt +1 -0
Dockerfile CHANGED
@@ -2,10 +2,10 @@ FROM python:3.12-slim
2
  WORKDIR /app
3
  COPY . .
4
 
5
- # Normal packages + accelerate (fixes the device_map error)
6
- RUN pip install --no-cache-dir fastapi uvicorn transformers pillow accelerate
7
 
8
- # PyTorch CPU version (required for the model)
9
  RUN pip install --no-cache-dir torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
10
 
11
  EXPOSE 7860
 
2
  WORKDIR /app
3
  COPY . .
4
 
5
+ # All required packages in one go
6
+ RUN pip install --no-cache-dir fastapi uvicorn transformers pillow accelerate python-multipart
7
 
8
+ # PyTorch CPU (required for the model)
9
  RUN pip install --no-cache-dir torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
10
 
11
  EXPOSE 7860
app.py CHANGED
@@ -1,36 +1,74 @@
 
1
  from fastapi import FastAPI, UploadFile, File, Form
2
- from pydantic import BaseModel
3
  from transformers import AutoModelForCausalLM, AutoProcessor
4
- import torch
5
  from PIL import Image
6
  import io
 
 
 
 
 
 
 
 
7
 
8
  app = FastAPI(title="Qwen3.5-0.8B OCR API")
9
 
 
 
10
  model_id = "Qwen/Qwen3.5-0.8B"
11
- model = AutoModelForCausalLM.from_pretrained(model_id, torch_dtype="auto", device_map="cpu")
12
- processor = AutoProcessor.from_pretrained(model_id)
 
 
 
 
 
 
 
 
 
 
13
 
 
 
 
 
 
 
 
 
 
14
 
 
 
15
  @app.post("/ocr")
16
  async def ocr(image: UploadFile = File(...), prompt: str = Form("Extract all text from this image accurately")):
17
- img = Image.open(io.BytesIO(await image.read()))
 
 
 
 
 
 
 
 
18
 
19
- messages = [{"role": "user", "content": [
20
- {"type": "image", "image": img},
21
- {"type": "text", "text": prompt}
22
- ]}]
23
 
24
- text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
25
- inputs = processor(text, images=img, return_tensors="pt").to("cpu")
26
 
27
- outputs = model.generate(**inputs, max_new_tokens=512, temperature=0.7)
28
- response = processor.decode(outputs[0], skip_special_tokens=True)
29
 
30
- return {"extracted_text": response}
 
 
31
 
32
 
33
- # Simple test endpoint
34
  @app.get("/")
35
  async def root():
36
- return {"message": "Qwen3.5-0.8B OCR API is running! Use POST /ocr with image file."}
 
1
+ import logging
2
  from fastapi import FastAPI, UploadFile, File, Form
 
3
  from transformers import AutoModelForCausalLM, AutoProcessor
 
4
  from PIL import Image
5
  import io
6
+ import torch
7
+
8
+ # === LOGGING SETUP ===
9
+ logging.basicConfig(
10
+ level=logging.INFO,
11
+ format="%(asctime)s | %(levelname)s | %(message)s"
12
+ )
13
+ logger = logging.getLogger(__name__)
14
 
15
  app = FastAPI(title="Qwen3.5-0.8B OCR API")
16
 
17
+ # === MODEL LOADING WITH LOGS ===
18
+ logger.info("=== STARTING MODEL LOAD ===")
19
  model_id = "Qwen/Qwen3.5-0.8B"
20
+ try:
21
+ model = AutoModelForCausalLM.from_pretrained(
22
+ model_id,
23
+ torch_dtype="auto",
24
+ device_map="cpu"
25
+ )
26
+ processor = AutoProcessor.from_pretrained(model_id)
27
+ logger.info("✅ Model loaded successfully!")
28
+ except Exception as e:
29
+ logger.error(f"❌ Model load failed: {e}")
30
+ raise
31
+
32
 
33
+ # === HEALTH CHECK ===
34
+ @app.get("/health")
35
+ async def health():
36
+ logger.info("Health check requested")
37
+ return {
38
+ "status": "healthy",
39
+ "model": model_id,
40
+ "message": "Qwen3.5-0.8B OCR API is running"
41
+ }
42
 
43
+
44
+ # === OCR ENDPOINT ===
45
  @app.post("/ocr")
46
  async def ocr(image: UploadFile = File(...), prompt: str = Form("Extract all text from this image accurately")):
47
+ logger.info(f"📸 OCR request received | Filename: {image.filename} | Prompt: {prompt[:100]}...")
48
+
49
+ try:
50
+ img = Image.open(io.BytesIO(await image.read()))
51
+
52
+ messages = [{"role": "user", "content": [
53
+ {"type": "image", "image": img},
54
+ {"type": "text", "text": prompt}
55
+ ]}]
56
 
57
+ text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
58
+ inputs = processor(text, images=img, return_tensors="pt").to("cpu")
 
 
59
 
60
+ outputs = model.generate(**inputs, max_new_tokens=512, temperature=0.3, do_sample=False)
61
+ response = processor.decode(outputs[0], skip_special_tokens=True)
62
 
63
+ logger.info("✅ OCR completed successfully")
64
+ return {"extracted_text": response}
65
 
66
+ except Exception as e:
67
+ logger.error(f"❌ OCR error: {e}")
68
+ return {"error": str(e)}
69
 
70
 
71
+ # === ROOT ===
72
  @app.get("/")
73
  async def root():
74
+ return {"message": "Qwen3.5-0.8B OCR API ready! Try /health or POST /ocr"}
requirements.txt CHANGED
@@ -3,4 +3,5 @@ uvicorn
3
  transformers
4
  pillow
5
  accelerate
 
6
  torch --index-url https://download.pytorch.org/whl/cpu
 
3
  transformers
4
  pillow
5
  accelerate
6
+ python-multipart
7
  torch --index-url https://download.pytorch.org/whl/cpu