Spaces:
Sleeping
Sleeping
Switch to HF Space as default backend, make Ollama optional
Browse files- README.md +44 -0
- app.py +62 -0
- deploy.sh +72 -0
- inference.py +108 -0
- requirements.txt +11 -0
- runtime.txt +1 -0
README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: KnowLedge Inference API
|
| 3 |
+
emoji: π
|
| 4 |
+
colorFrom: yellow
|
| 5 |
+
colorTo: green
|
| 6 |
+
sdk: gradio
|
| 7 |
+
python_version: 3.10.16
|
| 8 |
+
sdk_version: 4.44.1
|
| 9 |
+
app_file: app.py
|
| 10 |
+
pinned: true
|
| 11 |
+
license: apache-2.0
|
| 12 |
+
tags:
|
| 13 |
+
- gemma
|
| 14 |
+
- gemma-4
|
| 15 |
+
- education
|
| 16 |
+
- inference-api
|
| 17 |
+
- knowledge
|
| 18 |
+
- hackathon
|
| 19 |
+
---
|
| 20 |
+
|
| 21 |
+
# KnowLedge Inference API
|
| 22 |
+
|
| 23 |
+
Gemma 4 E2B + E4B inference backend for the
|
| 24 |
+
[KnowLedge](https://github.com/Charan-suresh/KnowLedge) learning ledger.
|
| 25 |
+
|
| 26 |
+
Part of the Kaggle Gemma 4 Good Hackathon submission.
|
| 27 |
+
|
| 28 |
+
## Endpoints
|
| 29 |
+
|
| 30 |
+
| Endpoint | Model | Used by |
|
| 31 |
+
|---|---|---|
|
| 32 |
+
| `/api/generate` | E2B or E4B | Scout (concept tagging), Sage (Socratic dialogue) |
|
| 33 |
+
| `/api/generate_vision` | E4B vision | Lens (handwriting verification) |
|
| 34 |
+
| `/api/health` | - | FastAPI health check |
|
| 35 |
+
|
| 36 |
+
## Not for direct use
|
| 37 |
+
|
| 38 |
+
This Space is called programmatically by the KnowLedge FastAPI backend
|
| 39 |
+
deployed on Render. It is not a chat interface.
|
| 40 |
+
|
| 41 |
+
## Deployment note
|
| 42 |
+
|
| 43 |
+
Deploy these files to a separate Hugging Face Space repository.
|
| 44 |
+
Do not import from the `knowledge/` package inside the Space app.
|
app.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI
|
| 2 |
+
from pydantic import BaseModel
|
| 3 |
+
import uvicorn
|
| 4 |
+
|
| 5 |
+
try:
|
| 6 |
+
from inference import generate_text, generate_with_image, health_check
|
| 7 |
+
except ModuleNotFoundError:
|
| 8 |
+
from .inference import generate_text, generate_with_image, health_check
|
| 9 |
+
|
| 10 |
+
app = FastAPI()
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class GenerateRequest(BaseModel):
|
| 14 |
+
prompt: str
|
| 15 |
+
model: str = "e2b"
|
| 16 |
+
max_tokens: int = 512
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class VisionRequest(BaseModel):
|
| 20 |
+
prompt: str
|
| 21 |
+
image_base64: str
|
| 22 |
+
max_tokens: int = 512
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
@app.get("/")
|
| 26 |
+
def root():
|
| 27 |
+
return {"message": "KnowLedge Inference API is running"}
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
@app.get("/api/health")
|
| 31 |
+
def health():
|
| 32 |
+
try:
|
| 33 |
+
return health_check()
|
| 34 |
+
except Exception as exc:
|
| 35 |
+
return {"status": "error", "error": str(exc)}
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
@app.post("/api/generate")
|
| 39 |
+
def generate(req: GenerateRequest):
|
| 40 |
+
try:
|
| 41 |
+
model_name = "e2b" if "e2b" in req.model.lower() else "e4b"
|
| 42 |
+
response = generate_text(model_name, req.prompt, req.max_tokens)
|
| 43 |
+
return {"response": response}
|
| 44 |
+
except Exception as exc:
|
| 45 |
+
return {"error": str(exc)}
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
@app.post("/api/generate_vision")
|
| 49 |
+
def generate_vision(req: VisionRequest):
|
| 50 |
+
try:
|
| 51 |
+
response = generate_with_image(
|
| 52 |
+
req.prompt,
|
| 53 |
+
req.image_base64,
|
| 54 |
+
req.max_tokens,
|
| 55 |
+
)
|
| 56 |
+
return {"response": response}
|
| 57 |
+
except Exception as exc:
|
| 58 |
+
return {"error": str(exc)}
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
if __name__ == "__main__":
|
| 62 |
+
uvicorn.run(app, host="0.0.0.0", port=7860)
|
deploy.sh
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
# KnowLedge - Hugging Face Space deployment guide
|
| 3 |
+
# Run each block manually in sequence.
|
| 4 |
+
# Prerequisites: huggingface_hub installed, HF account created
|
| 5 |
+
|
| 6 |
+
# -- Step 1: Install HF CLI -------------------------------------------------
|
| 7 |
+
pip install huggingface_hub
|
| 8 |
+
|
| 9 |
+
# -- Step 2: Login to Hugging Face ------------------------------------------
|
| 10 |
+
huggingface-cli login
|
| 11 |
+
# Paste your HF token when prompted
|
| 12 |
+
# Get token at: https://huggingface.co/settings/tokens
|
| 13 |
+
# Token needs "write" permission
|
| 14 |
+
|
| 15 |
+
# -- Step 3: Create the Space ------------------------------------------------
|
| 16 |
+
python3 - << 'EOF'
|
| 17 |
+
from huggingface_hub import HfApi
|
| 18 |
+
api = HfApi()
|
| 19 |
+
api.create_repo(
|
| 20 |
+
repo_id="knowledge-inference", # becomes: your-username/knowledge-inference
|
| 21 |
+
repo_type="space",
|
| 22 |
+
space_sdk="gradio",
|
| 23 |
+
private=False, # must be public for free ZeroGPU
|
| 24 |
+
exist_ok=True,
|
| 25 |
+
)
|
| 26 |
+
print("Space created successfully")
|
| 27 |
+
EOF
|
| 28 |
+
|
| 29 |
+
# -- Step 4: Upload Space files ----------------------------------------------
|
| 30 |
+
python3 - << 'EOF'
|
| 31 |
+
from huggingface_hub import HfApi
|
| 32 |
+
import os
|
| 33 |
+
|
| 34 |
+
api = HfApi()
|
| 35 |
+
space_id = f"{api.whoami()['name']}/knowledge-inference"
|
| 36 |
+
base_dir = os.getcwd()
|
| 37 |
+
|
| 38 |
+
files = ["app.py", "inference.py", "requirements.txt", "README.md"]
|
| 39 |
+
for f in files:
|
| 40 |
+
path = os.path.join(base_dir, f)
|
| 41 |
+
api.upload_file(
|
| 42 |
+
path_or_fileobj=path,
|
| 43 |
+
path_in_repo=f,
|
| 44 |
+
repo_id=space_id,
|
| 45 |
+
repo_type="space",
|
| 46 |
+
)
|
| 47 |
+
print(f"Uploaded: {f}")
|
| 48 |
+
|
| 49 |
+
print(f"\nSpace URL: https://huggingface.co/spaces/{space_id}")
|
| 50 |
+
print(f"API URL: https://{space_id.replace('/', '-')}.hf.space")
|
| 51 |
+
EOF
|
| 52 |
+
|
| 53 |
+
# -- Step 5: Enable ZeroGPU --------------------------------------------------
|
| 54 |
+
# Go to: https://huggingface.co/spaces/YOUR-USERNAME/knowledge-inference/settings
|
| 55 |
+
# Under "Space Hardware" -> select "ZeroGPU (NVIDIA H200)"
|
| 56 |
+
# This cannot be done via API - must be done in the HF web UI
|
| 57 |
+
echo "-> Manually enable ZeroGPU in Space settings:"
|
| 58 |
+
echo " https://huggingface.co/spaces/$(huggingface-cli whoami)/knowledge-inference/settings"
|
| 59 |
+
|
| 60 |
+
# -- Step 6: Get your Space API URL ------------------------------------------
|
| 61 |
+
# After deployment (~3-5 minutes for build), your endpoints are:
|
| 62 |
+
# Text: POST https://your-username-knowledge-inference.hf.space/api/generate
|
| 63 |
+
# Vision: POST https://your-username-knowledge-inference.hf.space/api/generate_vision
|
| 64 |
+
# Health: GET https://your-username-knowledge-inference.hf.space/api/health
|
| 65 |
+
|
| 66 |
+
# -- Step 7: Set Render env vars ---------------------------------------------
|
| 67 |
+
# In Render dashboard -> your service -> Environment:
|
| 68 |
+
# INFERENCE_BACKEND = hf_space
|
| 69 |
+
# HF_SPACE_URL = your-username/knowledge-inference
|
| 70 |
+
# Then trigger a manual redeploy on Render.
|
| 71 |
+
|
| 72 |
+
echo "Deployment complete. Set HF_SPACE_URL in Render and redeploy."
|
inference.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
KnowLedge Inference β inference.py
|
| 3 |
+
|
| 4 |
+
Runs entirely on CPU on the standard free HF Spaces tier (no ZeroGPU).
|
| 5 |
+
|
| 6 |
+
Model backends
|
| 7 |
+
--------------
|
| 8 |
+
E4B : unsloth/gemma-4-4b-it-GGUF loaded via llama-cpp-python (CPU, n_gpu_layers=0).
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
import logging
|
| 12 |
+
|
| 13 |
+
from huggingface_hub import hf_hub_download
|
| 14 |
+
|
| 15 |
+
# ββ spaces stub β GPU decorator is a no-op on the free CPU tier βββββββββββββββ
|
| 16 |
+
try:
|
| 17 |
+
import spaces
|
| 18 |
+
except ModuleNotFoundError:
|
| 19 |
+
class _SpacesStub:
|
| 20 |
+
@staticmethod
|
| 21 |
+
def GPU(duration: int = 0):
|
| 22 |
+
def decorator(fn):
|
| 23 |
+
return fn
|
| 24 |
+
return decorator
|
| 25 |
+
|
| 26 |
+
spaces = _SpacesStub()
|
| 27 |
+
|
| 28 |
+
# ββ llama-cpp-python ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 29 |
+
try:
|
| 30 |
+
from llama_cpp import Llama
|
| 31 |
+
except ModuleNotFoundError:
|
| 32 |
+
Llama = None
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
MODEL_REPO = "unsloth/gemma-4-4b-it-GGUF"
|
| 36 |
+
MODEL_FILE = "gemma-4-4b-it-Q4_K_M.gguf"
|
| 37 |
+
|
| 38 |
+
_llm = None
|
| 39 |
+
logger = logging.getLogger(__name__)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def load_model():
|
| 43 |
+
global _llm
|
| 44 |
+
if _llm is None:
|
| 45 |
+
if Llama is None:
|
| 46 |
+
raise RuntimeError(
|
| 47 |
+
"llama-cpp-python is not installed. "
|
| 48 |
+
"Add it to hf_space/requirements.txt."
|
| 49 |
+
)
|
| 50 |
+
model_path = str(hf_hub_download(
|
| 51 |
+
repo_id=MODEL_REPO,
|
| 52 |
+
filename=MODEL_FILE
|
| 53 |
+
))
|
| 54 |
+
|
| 55 |
+
_llm = Llama(
|
| 56 |
+
model_path=model_path,
|
| 57 |
+
n_ctx=4096,
|
| 58 |
+
n_threads=4
|
| 59 |
+
)
|
| 60 |
+
return _llm
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
@spaces.GPU(duration=90)
|
| 64 |
+
def generate_text(model_name: str, prompt: str, max_new_tokens: int = 512) -> str:
|
| 65 |
+
try:
|
| 66 |
+
llm = load_model()
|
| 67 |
+
|
| 68 |
+
output = llm(
|
| 69 |
+
str(prompt),
|
| 70 |
+
max_tokens=max_new_tokens,
|
| 71 |
+
temperature=0.7,
|
| 72 |
+
top_p=0.95
|
| 73 |
+
)
|
| 74 |
+
|
| 75 |
+
return output["choices"][0]["text"]
|
| 76 |
+
|
| 77 |
+
except Exception:
|
| 78 |
+
logger.exception("Text generation failed")
|
| 79 |
+
raise
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
@spaces.GPU(duration=120)
|
| 83 |
+
def generate_with_image(prompt: str, image_base64: str, max_new_tokens: int = 512) -> str:
|
| 84 |
+
try:
|
| 85 |
+
llm = load_model()
|
| 86 |
+
|
| 87 |
+
text_prompt = f"{prompt}\n\n[Image provided]"
|
| 88 |
+
|
| 89 |
+
output = llm(
|
| 90 |
+
str(text_prompt),
|
| 91 |
+
max_tokens=max_new_tokens,
|
| 92 |
+
temperature=0.7,
|
| 93 |
+
top_p=0.95
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
return output["choices"][0]["text"]
|
| 97 |
+
|
| 98 |
+
except Exception:
|
| 99 |
+
logger.exception("Vision generation failed")
|
| 100 |
+
raise
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
@spaces.GPU(duration=60)
|
| 104 |
+
def health_check() -> dict:
|
| 105 |
+
return {
|
| 106 |
+
"status": "ok",
|
| 107 |
+
"model_loaded": _llm is not None,
|
| 108 |
+
}
|
requirements.txt
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi==0.112.2
|
| 2 |
+
uvicorn==0.30.6
|
| 3 |
+
gradio==4.44.1
|
| 4 |
+
|
| 5 |
+
# Inference - E4B via llama-cpp-python (GGUF, CPU-only, no ZeroGPU needed)
|
| 6 |
+
--extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu
|
| 7 |
+
llama-cpp-python==0.2.90
|
| 8 |
+
|
| 9 |
+
# Utilities
|
| 10 |
+
Pillow
|
| 11 |
+
huggingface_hub==0.25.2
|
runtime.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
python-3.10.16
|