mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-03 22:13:01 +00:00
- The image pins DOCSGPT_HOME=/app: it ships no checkout, so the data home no longer depends on the working directory. - api, worker, beat and migrate print the data home and env file they resolved, so an API and a worker started from different directories show it. - The worker passes -Q only when asked; a bare worker consumes every configured queue, which honours EMBEDDINGS_QUEUE and DOCUMENT_PARSE_QUEUE. - The worker runs through celery.start and returns its exit code; click usage errors print usage and exit 2 instead of a traceback. - Windows: solo pool and no embedded scheduler (celery rejects -B there), with a pointer to the new `docsgpt beat` command, which runs the scheduler on its own. - prefetch_models and verify_offline parse their arguments, so --help is help rather than a model name. - A DOCSGPT_ENV_FILE that is not a file raises instead of booting with defaults.
124 lines
4.1 KiB
Python
124 lines
4.1 KiB
Python
"""Download the model artifacts a fresh container would otherwise fetch.
|
|
|
|
Run at image build time so a fresh container does not download on its first
|
|
request, and an air-gapped install works at all. Two things are warmed:
|
|
|
|
* Embedding models, into FastEmbed's cache. Both the legacy and the current
|
|
default are baked: an upgraded deployment keeps using mpnet until it runs
|
|
``reembed``, while a new one starts on granite.
|
|
* tiktoken's ``cl100k_base`` encoding, which token accounting uses on every
|
|
chat. tiktoken caches it under ``TIKTOKEN_CACHE_DIR`` (a temp dir when
|
|
unset), so the image sets that variable and this warms it.
|
|
|
|
Usage::
|
|
|
|
python -m docsgpt.scripts.prefetch_models # the defaults
|
|
python -m docsgpt.scripts.prefetch_models granite-311m # a subset
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import sys
|
|
from typing import List, Optional, Sequence
|
|
|
|
from docsgpt.vectorstore.model_registry import (
|
|
DEFAULT_LEGACY,
|
|
DEFAULT_NEW_INSTALL,
|
|
known_names,
|
|
resolve,
|
|
)
|
|
|
|
logger = logging.getLogger("prefetch_models")
|
|
|
|
#: Fetched when no names are given.
|
|
DEFAULT_MODELS = (DEFAULT_LEGACY, DEFAULT_NEW_INSTALL)
|
|
|
|
#: tiktoken encodings the application loads (``docsgpt.utils.get_encoding``).
|
|
TIKTOKEN_ENCODINGS = ("cl100k_base",)
|
|
|
|
|
|
def prefetch_tiktoken(names: Sequence[str] = TIKTOKEN_ENCODINGS) -> List[str]:
|
|
"""Warm tiktoken's cache for each encoding in ``names``.
|
|
|
|
Returns:
|
|
The encodings fetched.
|
|
"""
|
|
import tiktoken
|
|
|
|
for name in names:
|
|
logger.info("Fetching tiktoken encoding %s", name)
|
|
tiktoken.get_encoding(name)
|
|
return list(names)
|
|
|
|
|
|
def prefetch(names: Sequence[str], cache_dir: Optional[str] = None) -> List[str]:
|
|
"""Fetch each named model's artifacts.
|
|
|
|
Args:
|
|
names: Registry names or aliases.
|
|
cache_dir: FastEmbed cache directory; its default when omitted.
|
|
|
|
Returns:
|
|
The repositories actually fetched.
|
|
|
|
Raises:
|
|
SystemExit: If a name is not in the registry, since a silent skip at
|
|
build time becomes a download at run time on an offline host.
|
|
"""
|
|
from fastembed import TextEmbedding
|
|
from fastembed.common.model_description import ModelSource, PoolingType
|
|
|
|
pooling_types = {"cls": PoolingType.CLS, "mean": PoolingType.MEAN}
|
|
fetched: List[str] = []
|
|
for name in names:
|
|
spec = resolve(name)
|
|
if spec is None:
|
|
raise SystemExit(
|
|
f"Unknown embedding model {name!r}. Known: {', '.join(known_names())}"
|
|
)
|
|
if spec.provider != "fastembed":
|
|
logger.info("Skipping %s: served remotely, nothing to cache.", spec.name)
|
|
continue
|
|
logger.info("Fetching %s", spec.repo)
|
|
TextEmbedding.add_custom_model(
|
|
model=spec.repo,
|
|
pooling=pooling_types[spec.pooling],
|
|
normalization=spec.normalize,
|
|
sources=ModelSource(hf=spec.repo),
|
|
dim=spec.dimension,
|
|
model_file=spec.onnx_file,
|
|
)
|
|
kwargs = {"model_name": spec.repo}
|
|
if cache_dir:
|
|
kwargs["cache_dir"] = cache_dir
|
|
TextEmbedding(**kwargs)
|
|
fetched.append(spec.repo)
|
|
return fetched
|
|
|
|
|
|
def _parse(argv: Optional[Sequence[str]], prog: str, description: str) -> list[str]:
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(prog=prog, description=description)
|
|
parser.add_argument(
|
|
"models", nargs="*", help=f"embedding model names or aliases (default: {', '.join(DEFAULT_MODELS)})"
|
|
)
|
|
return parser.parse_args(argv).models or list(DEFAULT_MODELS)
|
|
|
|
|
|
def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s")
|
|
import os
|
|
|
|
names = _parse(argv, "prefetch-models", "Download the embedding models and the tiktoken encodings into the local caches.")
|
|
fetched = prefetch(names, os.environ.get("EMBEDDINGS_CACHE_DIR"))
|
|
logger.info("Cached %d model(s): %s", len(fetched), ", ".join(fetched))
|
|
encodings = prefetch_tiktoken()
|
|
logger.info("Cached tiktoken encoding(s): %s", ", ".join(encodings))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main(sys.argv[1:]))
|