Mehdi commited on
Commit ·
12c4c0f
1
Parent(s): 12edc45
fix: infinite spinner on question gen, 4-bit quant for low VRAM
Browse files- new_question returns error text in question_box (was empty string)
so JS polling can detect completion even on failure
- JS polling exits spinner on error instead of waiting forever
- @spaces.GPU timeout raised 60s -> 180s
- LLM loads in 4-bit (bitsandbytes) when VRAM < 17GB, bfloat16 otherwise
- Pre-load libnvJitLink.so.13 so bitsandbytes works locally with cu130
- Add bitsandbytes to requirements.txt
- app.py +17 -4
- model/llm.py +36 -2
- requirements.txt +1 -0
app.py
CHANGED
|
@@ -267,17 +267,18 @@ def load_pdf(pdf_file, language):
|
|
| 267 |
return chunks, s["loaded"](len(chunks)), 0, 0
|
| 268 |
|
| 269 |
|
| 270 |
-
@spaces.GPU(duration=
|
| 271 |
def new_question(chunks, language):
|
| 272 |
s = UI[language]
|
| 273 |
if not chunks:
|
| 274 |
-
return "", "", s["no_pdf"]
|
| 275 |
try:
|
| 276 |
chunk = random.choice(chunks)
|
| 277 |
question = generate_question(chunk, language=language)
|
| 278 |
return question, chunk, ""
|
| 279 |
except Exception as e:
|
| 280 |
-
|
|
|
|
| 281 |
|
| 282 |
|
| 283 |
@spaces.GPU(duration=120)
|
|
@@ -526,7 +527,19 @@ BRIDGE_JS = """() => {
|
|
| 526 |
|
| 527 |
if (S.waitingQ) {
|
| 528 |
const qEl = document.querySelector('#hidden-question-output textarea');
|
| 529 |
-
if (qEl && qEl.value && qEl.value !== S.prevQuestion)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 530 |
}
|
| 531 |
|
| 532 |
if (S.waitingFB) {
|
|
|
|
| 267 |
return chunks, s["loaded"](len(chunks)), 0, 0
|
| 268 |
|
| 269 |
|
| 270 |
+
@spaces.GPU(duration=180)
|
| 271 |
def new_question(chunks, language):
|
| 272 |
s = UI[language]
|
| 273 |
if not chunks:
|
| 274 |
+
return s["no_pdf"], "", s["no_pdf"]
|
| 275 |
try:
|
| 276 |
chunk = random.choice(chunks)
|
| 277 |
question = generate_question(chunk, language=language)
|
| 278 |
return question, chunk, ""
|
| 279 |
except Exception as e:
|
| 280 |
+
err = f"Erreur : {e}"
|
| 281 |
+
return err, "", err
|
| 282 |
|
| 283 |
|
| 284 |
@spaces.GPU(duration=120)
|
|
|
|
| 527 |
|
| 528 |
if (S.waitingQ) {
|
| 529 |
const qEl = document.querySelector('#hidden-question-output textarea');
|
| 530 |
+
if (qEl && qEl.value && qEl.value !== S.prevQuestion) {
|
| 531 |
+
const val = qEl.value;
|
| 532 |
+
const isErr = val.startsWith('Erreur') || val.startsWith('⚠️');
|
| 533 |
+
if (isErr) {
|
| 534 |
+
S.waitingQ = false;
|
| 535 |
+
const ql=$('q-loading'), nq=$('new-q-btn');
|
| 536 |
+
if(ql) ql.classList.add('hidden');
|
| 537 |
+
if(nq) nq.disabled=false;
|
| 538 |
+
applyStatus(val);
|
| 539 |
+
} else {
|
| 540 |
+
showQuestion(val);
|
| 541 |
+
}
|
| 542 |
+
}
|
| 543 |
}
|
| 544 |
|
| 545 |
if (S.waitingFB) {
|
model/llm.py
CHANGED
|
@@ -22,13 +22,39 @@ Public API:
|
|
| 22 |
"""
|
| 23 |
|
| 24 |
import os
|
|
|
|
| 25 |
import torch
|
| 26 |
from functools import lru_cache
|
| 27 |
-
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
|
| 28 |
|
| 29 |
DEFAULT_MODEL_ID = "openbmb/MiniCPM4-8B"
|
| 30 |
DEFAULT_MAX_NEW_TOKENS = 512
|
| 31 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
|
| 33 |
class LLM:
|
| 34 |
"""Thin wrapper around a HuggingFace text-generation pipeline."""
|
|
@@ -36,9 +62,17 @@ class LLM:
|
|
| 36 |
def __init__(self, model_id: str, device: str):
|
| 37 |
self.model_id = model_id
|
| 38 |
self._tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
model = AutoModelForCausalLM.from_pretrained(
|
| 40 |
model_id,
|
| 41 |
-
|
|
|
|
| 42 |
device_map=device,
|
| 43 |
trust_remote_code=True,
|
| 44 |
)
|
|
|
|
| 22 |
"""
|
| 23 |
|
| 24 |
import os
|
| 25 |
+
import ctypes
|
| 26 |
import torch
|
| 27 |
from functools import lru_cache
|
| 28 |
+
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline, BitsAndBytesConfig
|
| 29 |
|
| 30 |
DEFAULT_MODEL_ID = "openbmb/MiniCPM4-8B"
|
| 31 |
DEFAULT_MAX_NEW_TOKENS = 512
|
| 32 |
|
| 33 |
+
# Pre-load libnvJitLink.so.13 bundled with the nvidia-cu13 wheel so that
|
| 34 |
+
# bitsandbytes can find it when it calls dlopen internally.
|
| 35 |
+
def _preload_nvjitlink() -> None:
|
| 36 |
+
try:
|
| 37 |
+
import site
|
| 38 |
+
for sp in site.getsitepackages():
|
| 39 |
+
candidate = os.path.join(sp, "nvidia", "cu13", "lib", "libnvJitLink.so.13")
|
| 40 |
+
if os.path.exists(candidate):
|
| 41 |
+
ctypes.CDLL(candidate)
|
| 42 |
+
return
|
| 43 |
+
except Exception:
|
| 44 |
+
pass
|
| 45 |
+
|
| 46 |
+
_preload_nvjitlink()
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def _build_quantization_config(vram_gb: float):
|
| 50 |
+
if vram_gb < 17:
|
| 51 |
+
try:
|
| 52 |
+
import bitsandbytes # noqa: F401
|
| 53 |
+
return BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_compute_dtype=torch.bfloat16)
|
| 54 |
+
except Exception:
|
| 55 |
+
pass
|
| 56 |
+
return None
|
| 57 |
+
|
| 58 |
|
| 59 |
class LLM:
|
| 60 |
"""Thin wrapper around a HuggingFace text-generation pipeline."""
|
|
|
|
| 62 |
def __init__(self, model_id: str, device: str):
|
| 63 |
self.model_id = model_id
|
| 64 |
self._tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)
|
| 65 |
+
|
| 66 |
+
vram_gb = 0.0
|
| 67 |
+
if torch.cuda.is_available():
|
| 68 |
+
vram_gb = torch.cuda.get_device_properties(0).total_memory / 1024**3
|
| 69 |
+
quant_cfg = _build_quantization_config(vram_gb)
|
| 70 |
+
print(f"[LLM] VRAM={vram_gb:.1f}GB — {'4-bit quant' if quant_cfg else 'bfloat16'}")
|
| 71 |
+
|
| 72 |
model = AutoModelForCausalLM.from_pretrained(
|
| 73 |
model_id,
|
| 74 |
+
quantization_config=quant_cfg,
|
| 75 |
+
torch_dtype=torch.bfloat16 if quant_cfg is None else None,
|
| 76 |
device_map=device,
|
| 77 |
trust_remote_code=True,
|
| 78 |
)
|
requirements.txt
CHANGED
|
@@ -16,3 +16,4 @@ transformers==4.57.1
|
|
| 16 |
accelerate>=0.31.0
|
| 17 |
sentencepiece>=0.2.0
|
| 18 |
protobuf>=4.25.0
|
|
|
|
|
|
| 16 |
accelerate>=0.31.0
|
| 17 |
sentencepiece>=0.2.0
|
| 18 |
protobuf>=4.25.0
|
| 19 |
+
bitsandbytes>=0.43.0
|