plant_doc / app.py
Rady10's picture
Update app.py
25ef190 verified
Raw
History Blame Contribute Delete
3.62 kB
import os
import io
import threading
import torch
from PIL import Image
from fastapi import FastAPI, UploadFile, File, Form
from fastapi.responses import JSONResponse
from unsloth import FastVisionModel
from transformers import TextIteratorStreamer
from groq import Groq
MODEL_REPO = "Rady10/Plant-Disease-Qwen3VL-2B"
MAX_SEQ_LENGTH = 512
VISION_MAX_NEW_TOKENS = 256
SUMMARY_MAX_TOKENS = 220
GROQ_API_KEY = os.environ["GROQ_API_KEY"]
GROQ_MODEL = "llama-3.3-70b-versatile"
print("Loading vision model...")
model, tokenizer = FastVisionModel.from_pretrained(
MODEL_REPO,
load_in_4bit=True,
max_seq_length=MAX_SEQ_LENGTH,
use_gradient_checkpointing="unsloth",
)
FastVisionModel.for_inference(model)
print("Vision model loaded.")
groq_client = Groq(api_key=GROQ_API_KEY)
VISION_SYSTEM_PROMPT = """
You are a professional plant disease expert.
You identify diseases from plant images and answer agriculture questions.
"""
SUMMARY_SYSTEM_PROMPT = """
You are an agriculture assistant.
Rewrite the diagnosis in simple Egyptian Arabic.
Rules:
- 3-6 short sentences.
- Mention disease.
- Mention likely cause.
- Mention treatment.
- Do not invent information.
"""
def run_vision_model(image, question):
messages = [
{
"role": "system",
"content": [
{
"type":"text",
"text":VISION_SYSTEM_PROMPT
}
]
},
{
"role":"user",
"content":[
{
"type":"image",
"image":image
},
{
"type":"text",
"text":question or "Diagnose this plant."
}
]
}
]
prompt = tokenizer.apply_chat_template(
messages,
add_generation_prompt=True
)
inputs = tokenizer(
image,
prompt,
add_special_tokens=False,
return_tensors="pt"
)
if torch.cuda.is_available():
inputs = inputs.to("cuda")
streamer = TextIteratorStreamer(
tokenizer,
skip_prompt=True,
skip_special_tokens=True,
)
kwargs = dict(
**inputs,
streamer=streamer,
max_new_tokens=VISION_MAX_NEW_TOKENS,
temperature=0.7,
top_p=0.9,
repetition_penalty=1.15,
)
thread = threading.Thread(
target=model.generate,
kwargs=kwargs,
)
thread.start()
text = ""
for piece in streamer:
text += piece
return text.strip()
def summarize_with_groq(raw, question):
response = groq_client.chat.completions.create(
model=GROQ_MODEL,
messages=[
{
"role":"system",
"content":SUMMARY_SYSTEM_PROMPT
},
{
"role":"user",
"content":f"""
Question:
{question}
Diagnosis:
{raw}
"""
}
],
max_tokens=SUMMARY_MAX_TOKENS,
temperature=0.6,
)
return response.choices[0].message.content
app = FastAPI(title="Plant Disease API")
@app.post("/analyze")
async def analyze(
image: UploadFile = File(...),
question: str = Form("")
):
image_bytes = await image.read()
image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
raw = run_vision_model(image, question)
summary = summarize_with_groq(raw, question)
return JSONResponse(
{
"question": question,
"summary": summary,
"technical_diagnosis": raw
}
)