File size: 2,508 Bytes
86e797b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
"""Optional ZeroGPU query encoding; CPU ranking and Gemini stay outside the GPU lease."""

import json
import os
from pathlib import Path

# Import spaces before torch/transformers so ZeroGPU can install its CUDA hooks.
if os.environ.get("VDR_QUERY_DEVICE", "cpu") == "zerogpu":
    import spaces
else:
    spaces = None

_encoder = None


def ensure_models(data_dir, model_info):
    """Space repositories omit weights; fetch only the pinned official snapshots."""
    from huggingface_hub import snapshot_download

    for kind in ("adapter", "base"):
        root = data_dir / "models" / model_info[f"{kind}_path"]
        if not list(root.glob("*.safetensors")):
            snapshot_download(
                repo_id=model_info["model_id" if kind == "adapter" else "base_model_id"],
                revision=model_info[f"{kind}_revision"],
                local_dir=root,
                allow_patterns=["*.json", "*.safetensors", "*.jinja", "README.md", "LICENSE*"],
                token=False,
            )


def encode_query(query):
    if _encoder is None:
        raise RuntimeError("Initialize the ZeroGPU encoder at application startup.")
    return _encoder.encode(query=query)


if spaces is not None:
    encode_query = spaces.GPU(duration=10)(encode_query)


def create_service(data_dir=Path("data")):
    from visual_doc_rag.serve import AnswerService

    device = os.environ.get("VDR_QUERY_DEVICE", "cpu")
    if device == "cpu":
        manifest_path = data_dir / "hr/indexes/visual.json"
        if (data_dir / "hr/demo.json").exists() and manifest_path.exists():
            manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
            ensure_models(data_dir, manifest["signature"]["model"])
        return AnswerService(data_dir=data_dir)
    if device != "zerogpu" or spaces is None:
        raise ValueError("VDR_QUERY_DEVICE must be cpu or zerogpu (requires the hosting extra).")

    from visual_doc_rag.retrieve import PageRetriever
    from visual_doc_rag.visual_index import Encoder

    global _encoder
    manifest = json.loads((data_dir / "hr/indexes/visual.json").read_text(encoding="utf-8"))
    ensure_models(data_dir, manifest["signature"]["model"])
    # Official ZeroGPU guidance: place the model on CUDA at startup, before a GPU call.
    _encoder = Encoder(data_dir, manifest["signature"]["model"], "cuda")
    retriever = PageRetriever(data_dir=data_dir, query_encoder=encode_query)
    return AnswerService(data_dir=data_dir, retriever=retriever)