Spaces:
Running
Running
Commit ·
7e2a824
1
Parent(s): f09a85f
Add Hugging Face Serverless API option (Option 3)
Browse files- ai_router.py +3 -0
- config.py +2 -1
- models/hf_api_model.py +72 -0
- requirements.txt +1 -0
- static/app.js +2 -2
- templates/index.html +1 -0
ai_router.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
from models.blip_model import blip_answer
|
| 2 |
from models.reasoning_model import reasoning_answer
|
| 3 |
from models.gemini_vision import gemini_vision_answer
|
|
|
|
| 4 |
|
| 5 |
try:
|
| 6 |
from deep_translator import GoogleTranslator
|
|
@@ -21,6 +22,8 @@ def route_model(model_choice, image, question, lang="en"):
|
|
| 21 |
cap, ans, exp = reasoning_answer(image, question)
|
| 22 |
elif model_choice == "gemini":
|
| 23 |
cap, ans, exp = gemini_vision_answer(image, question, lang)
|
|
|
|
|
|
|
| 24 |
else:
|
| 25 |
cap, ans, exp = "Unknown", "Invalid", "Invalid"
|
| 26 |
|
|
|
|
| 1 |
from models.blip_model import blip_answer
|
| 2 |
from models.reasoning_model import reasoning_answer
|
| 3 |
from models.gemini_vision import gemini_vision_answer
|
| 4 |
+
from models.hf_api_model import hf_api_reasoning
|
| 5 |
|
| 6 |
try:
|
| 7 |
from deep_translator import GoogleTranslator
|
|
|
|
| 22 |
cap, ans, exp = reasoning_answer(image, question)
|
| 23 |
elif model_choice == "gemini":
|
| 24 |
cap, ans, exp = gemini_vision_answer(image, question, lang)
|
| 25 |
+
elif model_choice == "hf_api":
|
| 26 |
+
cap, ans, exp = hf_api_reasoning(image, question, lang)
|
| 27 |
else:
|
| 28 |
cap, ans, exp = "Unknown", "Invalid", "Invalid"
|
| 29 |
|
config.py
CHANGED
|
@@ -12,4 +12,5 @@ GEMINI_API_KEY = os.getenv("GEMINI_API_KEY", "AIzaSyB1fMfnnp2etuOVWiLrecdMp3_0Gb
|
|
| 12 |
|
| 13 |
# ===== Security Limits =====
|
| 14 |
MAX_IMAGE_SIZE_MB = 5
|
| 15 |
-
RATE_LIMIT_PER_MINUTE = 30
|
|
|
|
|
|
| 12 |
|
| 13 |
# ===== Security Limits =====
|
| 14 |
MAX_IMAGE_SIZE_MB = 5
|
| 15 |
+
RATE_LIMIT_PER_MINUTE = 30
|
| 16 |
+
HF_TOKEN = os.getenv("HF_TOKEN", "")
|
models/hf_api_model.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import io
|
| 3 |
+
import base64
|
| 4 |
+
import requests
|
| 5 |
+
from config import HF_TOKEN
|
| 6 |
+
|
| 7 |
+
def encode_image(image):
|
| 8 |
+
buffered = io.BytesIO()
|
| 9 |
+
# Convert image to RGB if not already to prevent JPEG save errors
|
| 10 |
+
if image.mode != "RGB":
|
| 11 |
+
image = image.convert("RGB")
|
| 12 |
+
image.save(buffered, format="JPEG")
|
| 13 |
+
return base64.b64encode(buffered.getvalue()).decode('utf-8')
|
| 14 |
+
|
| 15 |
+
def hf_api_reasoning(image, question, lang="en"):
|
| 16 |
+
# Using Llama 3.2 11B Vision Instruct via Serverless
|
| 17 |
+
model_id = "meta-llama/Llama-3.2-11B-Vision-Instruct"
|
| 18 |
+
api_url = f"https://api-inference.huggingface.co/models/{model_id}/v1/chat/completions"
|
| 19 |
+
|
| 20 |
+
token = HF_TOKEN or os.getenv("HF_TOKEN")
|
| 21 |
+
|
| 22 |
+
if not token:
|
| 23 |
+
return "Setup Required", "Hugging Face Access Token is missing.", "Please ensure HF_TOKEN is set in your environment or config.py"
|
| 24 |
+
|
| 25 |
+
headers = {
|
| 26 |
+
"Authorization": f"Bearer {token}",
|
| 27 |
+
"Content-Type": "application/json"
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
base64_img = f"data:image/jpeg;base64,{encode_image(image)}"
|
| 31 |
+
|
| 32 |
+
payload = {
|
| 33 |
+
"model": model_id,
|
| 34 |
+
"messages": [
|
| 35 |
+
{
|
| 36 |
+
"role": "user",
|
| 37 |
+
"content": [
|
| 38 |
+
{"type": "image_url", "image_url": {"url": base64_img}},
|
| 39 |
+
{"type": "text", "text": f"Please provide an accurate answer to the following question. First, provide a very short, direct final answer. Second, provide a detailed explanation.\\nQuestion: {question}"}
|
| 40 |
+
]
|
| 41 |
+
}
|
| 42 |
+
],
|
| 43 |
+
"max_tokens": 250
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
try:
|
| 47 |
+
response = requests.post(api_url, headers=headers, json=payload, timeout=60)
|
| 48 |
+
|
| 49 |
+
if response.status_code == 429:
|
| 50 |
+
return "Rate Limited", "Hugging Face Serverless API is currently busy.", "You have hit the free tier rate limit. Please try again in a few minutes."
|
| 51 |
+
elif response.status_code == 503:
|
| 52 |
+
return "Model Loading", "The AI model is currently booting up on Hugging Face servers.", "It takes about 20-30 seconds to wake up the 11-Billion parameter model. Try again in 20 seconds!"
|
| 53 |
+
|
| 54 |
+
response.raise_for_status()
|
| 55 |
+
data = response.json()
|
| 56 |
+
|
| 57 |
+
raw_text = data["choices"][0]["message"]["content"]
|
| 58 |
+
|
| 59 |
+
return "Llama-3.2-11B-Vision", raw_text, "Processed via Hugging Face Serverless GPU API"
|
| 60 |
+
|
| 61 |
+
except requests.exceptions.Timeout:
|
| 62 |
+
return "Timeout", "The Hugging Face server took too long to respond.", "This can happen if the model is waking up. Please try again."
|
| 63 |
+
except Exception as e:
|
| 64 |
+
error_msg = str(e)
|
| 65 |
+
try:
|
| 66 |
+
# Attempt to parse detailed error from huggingface
|
| 67 |
+
error_data = response.json()
|
| 68 |
+
if "error" in error_data:
|
| 69 |
+
error_msg = error_data["error"]
|
| 70 |
+
except:
|
| 71 |
+
pass
|
| 72 |
+
return "API Error", "Connection failed.", f"{error_msg}"
|
requirements.txt
CHANGED
|
@@ -6,3 +6,4 @@ torch==2.2.1
|
|
| 6 |
transformers==4.38.2
|
| 7 |
google-genai==0.3.0
|
| 8 |
deep-translator==1.11.4
|
|
|
|
|
|
| 6 |
transformers==4.38.2
|
| 7 |
google-genai==0.3.0
|
| 8 |
deep-translator==1.11.4
|
| 9 |
+
requests>=2.28.0
|
static/app.js
CHANGED
|
@@ -354,7 +354,7 @@ async function fetchLogs() {
|
|
| 354 |
appState.logs = data.logs || [];
|
| 355 |
appState.stats.total = appState.logs.length;
|
| 356 |
appState.stats.local = appState.logs.filter(l => ['local', 'blip', 'reasoning'].includes(l.model.toLowerCase())).length;
|
| 357 |
-
appState.stats.gemini = appState.logs.filter(l => ['gemini', 'external'].includes(l.model.toLowerCase())).length;
|
| 358 |
|
| 359 |
updateDashboardView();
|
| 360 |
} catch(err) {
|
|
@@ -378,7 +378,7 @@ function renderLogs(filter = '') {
|
|
| 378 |
tr.innerHTML = `
|
| 379 |
<td>${log.timestamp}</td>
|
| 380 |
<td><span class="user-badge" style="color: var(--text-secondary);"><i class="fa-solid fa-user"></i> ${log.user}</span></td>
|
| 381 |
-
<td><span style="color: ${log.model.includes(
|
| 382 |
<td>${log.question.length > 50 ? log.question.substring(0, 50) + '...' : log.question}</td>
|
| 383 |
`;
|
| 384 |
els.logsBody.appendChild(tr);
|
|
|
|
| 354 |
appState.logs = data.logs || [];
|
| 355 |
appState.stats.total = appState.logs.length;
|
| 356 |
appState.stats.local = appState.logs.filter(l => ['local', 'blip', 'reasoning'].includes(l.model.toLowerCase())).length;
|
| 357 |
+
appState.stats.gemini = appState.logs.filter(l => ['gemini', 'external', 'hf_api'].includes(l.model.toLowerCase())).length;
|
| 358 |
|
| 359 |
updateDashboardView();
|
| 360 |
} catch(err) {
|
|
|
|
| 378 |
tr.innerHTML = `
|
| 379 |
<td>${log.timestamp}</td>
|
| 380 |
<td><span class="user-badge" style="color: var(--text-secondary);"><i class="fa-solid fa-user"></i> ${log.user}</span></td>
|
| 381 |
+
<td><span style="color: ${['gemini', 'hf_api'].some(m => log.model.includes(m)) ? 'var(--accent)' : 'var(--success)'}">${log.model.toUpperCase().replace('_', ' ')}</span></td>
|
| 382 |
<td>${log.question.length > 50 ? log.question.substring(0, 50) + '...' : log.question}</td>
|
| 383 |
`;
|
| 384 |
els.logsBody.appendChild(tr);
|
templates/index.html
CHANGED
|
@@ -97,6 +97,7 @@
|
|
| 97 |
<select id="model-selector">
|
| 98 |
<option value="local">BLIP + FLAN (Local)</option>
|
| 99 |
<option value="gemini" id="gemini-option">Gemini 3.1 Flash Vision (High Quota)</option>
|
|
|
|
| 100 |
</select>
|
| 101 |
</div>
|
| 102 |
<div class="control-group" style="flex: 1;">
|
|
|
|
| 97 |
<select id="model-selector">
|
| 98 |
<option value="local">BLIP + FLAN (Local)</option>
|
| 99 |
<option value="gemini" id="gemini-option">Gemini 3.1 Flash Vision (High Quota)</option>
|
| 100 |
+
<option value="hf_api" id="hf_api-option">HF Llama 3.2 Vision API (Flawless)</option>
|
| 101 |
</select>
|
| 102 |
</div>
|
| 103 |
<div class="control-group" style="flex: 1;">
|