feat: publish the backend to PyPI as docsgpt

pip install docsgpt (extras: docling, milvus) installs the backend with a
docsgpt command: api, worker, migrate, prefetch-models, verify-offline,
reembed. Second step of the PyPI work after the package rename.

- hatchling build; the version comes from docsgpt/version.py. The wheel is
  the docsgpt package with the data it reads at runtime (prompts, model
  catalogs, seed config, alembic.ini and migrations) and without the
  Dockerfile, the exported requirements, the sample index and local runtime
  data. The application import alias stays checkout-only. uv sync installs
  the package editable now that [tool.uv] package = false is gone.
- docsgpt/cli.py: api (gunicorn + BoundedDrainUvicornWorker with the image's
  flags, --reload for uvicorn), worker (Celery worker with beat embedded,
  --no-beat/-Q/--concurrency/--pool, solo pool on macOS), migrate, and
  argument pass-through to the maintenance scripts. --help imports no app.
- docsgpt/core/paths.py: runtime data lives in a data home (DOCSGPT_HOME,
  else the checkout, else cwd); DOCSGPT_ENV_FILE overrides the env file.
  Settings, the dotenv load, LocalStorage and the internal upload route use
  it instead of "three directories above this file", which is site-packages
  for an installed package. A checkout and the Docker image behave as before.
- [project] dependencies are compatible ranges so the package installs next
  to other packages; uv.lock resolves to the same versions and the exported
  requirements files are unchanged.
- package-build.yml builds and checks the wheel on PRs and installs it into
  a clean venv; pypi-publish.yml publishes on a published release through
  trusted publishing (environment pypi), or to TestPyPI on a manual run.
- Docs: Deploying -> Install with pip. AGENTS.md notes the package.
This commit is contained in:
Alex committed 2026-09-07 14:40:35 +01:00
1 parent af284218a0
commit bea26336f8
18 files changed
+844 -175

No files matched your search

+140 -80
View File
@@ -1,58 +1,80 @@
[build-system]
requires = ["hatchling>=1.27,<2"]
build-backend = "hatchling.build"
[project]
name = "docsgpt"
# Bump together with docsgpt/version.py and frontend/package.json.
version = "0.19.0"
# The version is read from docsgpt/version.py (the release workflow reads the
# same file); bump it there.
dynamic = ["version"]
description = "DocsGPT backend: chat with your documents, agents, and tools."
readme = "README.md"
requires-python = ">=3.12"
license = { file = "LICENSE" }
license = "MIT"
license-files = ["LICENSE"]
authors = [{ name = "Arc53" }]
keywords = ["rag", "llm", "agents", "documents", "chat", "search"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Environment :: Web Environment",
"Framework :: Flask",
"Intended Audience :: Developers",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
]
# Direct dependencies only. Transitive pins live in uv.lock; the pip-facing
# files under docsgpt/ (requirements*.txt) are exported from that lock by
# scripts/export_requirements.sh and must not be edited by hand.
# Direct dependencies only, as compatible ranges: the floor is the version
# uv.lock pins, the ceiling the next major (next minor below 1.0), so the
# PyPI package installs next to other packages. The pip-facing files under
# docsgpt/ (requirements*.txt) are exported from the lock by
# scripts/export_requirements.sh and must not be edited by hand; Docker, CI
# and the checkout install those exact versions.
dependencies = [
"a2wsgi==1.10.10",
"a2wsgi>=1.10.10,<2",
"alembic>=1.13,<2",
"anthropic==0.121.0",
"beautifulsoup4==4.15.0",
"boto3==1.43.67",
"cel-python==0.5.0",
"celery==5.6.3",
"celery-redbeat==2.4.2",
"croniter==6.2.4",
"cryptography==50.0.0",
"dataclasses-json==0.6.7",
"daytona==0.205.1",
"ddgs>=8.0.0",
"defusedxml==0.7.1",
"docx2txt==0.9",
"elevenlabs==2.62.0",
"faiss-cpu==1.15.0",
"fast-ebook",
"anthropic>=0.121.0,<0.122",
"beautifulsoup4>=4.15.0,<5",
"boto3>=1.43.67,<2",
"cel-python>=0.5.0,<0.6",
"celery>=5.6.3,<6",
"celery-redbeat>=2.4.2,<3",
"croniter>=6.2.4,<7",
"cryptography>=50.0.0,<51",
"dataclasses-json>=0.6.7,<0.7",
"daytona>=0.205.1,<0.206",
"ddgs>=8.0.0,<10",
"defusedxml>=0.7.1,<0.8",
"docx2txt>=0.9,<0.10",
"elevenlabs>=2.62.0,<3",
"faiss-cpu>=1.15.0,<2",
"fast-ebook>=0.2.0,<0.3",
# Default document converter (DOC_PARSER_ENGINE=anydoc): a Rust extension
# with no model downloads. The docling engine is the `docling` extra.
"firecrawl-anydoc==0.2.3",
"fastembed==0.8.0",
"fastmcp==3.4.6",
"Flask==3.1.3",
"flask-restx==1.3.2",
"google-api-python-client==2.198.0",
"google-auth-oauthlib==1.4.0",
"google-genai==2.17.0",
"gTTS==2.5.4",
"gunicorn==26.0.0",
"jinja2==3.1.6",
"kombu==5.6.2",
"markdownify==1.2.3",
"msal==1.37.0",
"networkx==3.6.1",
"numpy==2.5.1",
"firecrawl-anydoc>=0.2.3,<0.3",
"fastembed>=0.8.0,<0.9",
"fastmcp>=3.4.6,<4",
"Flask>=3.1.3,<4",
"flask-restx>=1.3.2,<2",
"google-api-python-client>=2.198.0,<3",
"google-auth-oauthlib>=1.4.0,<2",
"google-genai>=2.17.0,<3",
"gTTS>=2.5.4,<3",
"gunicorn>=26.0.0,<27",
"jinja2>=3.1.6,<4",
"kombu>=5.6.2,<6",
"markdownify>=1.2.3,<2",
"msal>=1.37.0,<2",
"networkx>=3.6.1,<4",
"numpy>=2.5.1,<3",
# fastembed's runtime: local embeddings execute on it.
"onnxruntime==1.28.0",
"openai==2.53.0",
"openapi3-parser==1.1.22",
"onnxruntime>=1.28.0,<2",
"openai>=2.53.0,<3",
"openapi3-parser>=1.1.22,<2",
# pandas reads .xlsx through openpyxl but does not depend on it.
"openpyxl==3.1.5",
"openpyxl>=3.1.5,<4",
"opentelemetry-distro>=0.50b0,<1",
"opentelemetry-exporter-otlp>=1.29.0,<2",
"opentelemetry-instrumentation-celery>=0.50b0,<1",
@@ -63,59 +85,71 @@ dependencies = [
"opentelemetry-instrumentation-requests>=0.50b0,<1",
"opentelemetry-instrumentation-sqlalchemy>=0.50b0,<1",
"opentelemetry-instrumentation-starlette>=0.50b0,<1",
"pandas==3.0.5",
"pdf2image>=1.17.0",
"pandas>=3.0.5,<4",
"pdf2image>=1.17.0,<2",
"pgvector>=0.5,<1",
"pillow==12.3.0",
"praw==8.0.2",
"pillow>=12.3.0,<13",
"praw>=8.0.2,<9",
"psycopg[binary,pool]>=3.1,<4",
"pydantic",
"pydantic-settings",
"pypdf==6.15.0",
"pypdfium2==5.12.1",
"python-dateutil==2.9.0.post0",
"python-dotenv",
"python-jose==3.5.0",
"python-pptx==1.0.2",
"PyYAML",
"qdrant-client==1.19.0",
"redis==7.4.0",
"requests==2.34.2",
"retry==0.9.2",
"pydantic>=2.13.5,<3",
"pydantic-settings>=2.15.0,<3",
"pypdf>=6.15.0,<7",
"pypdfium2>=5.12.1,<6",
"python-dateutil>=2.9.0,<3",
"python-dotenv>=1.2.3,<2",
"python-jose>=3.5.0,<4",
"python-pptx>=1.0.2,<2",
"PyYAML>=6.0.3,<7",
"qdrant-client>=1.19.0,<2",
"redis>=7.4.0,<8",
"requests>=2.34.2,<3",
"retry>=0.9.2,<0.10",
"sqlalchemy>=2.0,<3",
"starlette>=1.0,<2",
"tiktoken==0.13.0",
"tldextract==5.3.2",
"tokenizers==0.22.2",
"tqdm==4.67.3",
"tiktoken>=0.13.0,<0.14",
"tldextract>=5.3.2,<6",
"tokenizers>=0.22.2,<0.23",
"tqdm>=4.67.3,<5",
"uvicorn[standard]>=0.30,<1",
"uvicorn-worker>=0.4,<1",
"websocket-client==1.9.0",
"werkzeug>=3.1.0",
"websocket-client>=1.9.0,<2",
"werkzeug>=3.1.0,<4",
]
[project.optional-dependencies]
# Docling parser engine: DOC_PARSER_ENGINE=docling, the docling OCR backend
# (layout-model hybrid OCR, ocrmac/rapidocr engines), .adoc/.vtt/.xml
# attachment parsing, and read_document's `structured` output. Pulls torch and
# transformers; on Linux torch resolves from the CPU-only PyTorch index (see
# [tool.uv.sources]) so the extra does not drag the CUDA stack in.
# transformers; with uv on Linux torch resolves from the CPU-only PyTorch index
# (see [tool.uv.sources]) so the extra does not drag the CUDA stack in. pip
# users on Linux get the same by adding
# --extra-index-url https://download.pytorch.org/whl/cpu.
docling = [
"docling==2.119.0",
"rapidocr==3.9.2",
# docling's model stack. Declared here (not left transitive) so the pins
"docling>=2.119.0,<3",
"rapidocr>=3.9.2,<4",
# docling's model stack. Declared here (not left transitive) so the floors
# hold and the CPU index source below applies. transformers is capped by
# docling-core at <5.9: 5.9+ breaks the PDF layout model on Apple Silicon.
"torch==2.11.0",
"torchvision==0.26.0",
"transformers==5.8.1",
"torch>=2.11.0,<3",
"torchvision>=0.26.0,<0.27",
"transformers>=5.8.1,<5.9",
]
# VECTOR_STORE=milvus. milvus-lite (the embedded server) pulls pyarrow.
milvus = [
"pymilvus==3.0.1",
"milvus-lite==3.2.0; sys_platform != 'win32'",
"pymilvus>=3.0.1,<4",
"milvus-lite>=3.2.0,<4; sys_platform != 'win32'",
]
[project.scripts]
docsgpt = "docsgpt.cli:main"
[project.urls]
Homepage = "https://www.docsgpt.cloud/"
Documentation = "https://docs.docsgpt.cloud/"
Repository = "https://github.com/arc53/DocsGPT"
Issues = "https://github.com/arc53/DocsGPT/issues"
Changelog = "https://github.com/arc53/DocsGPT/releases"
[dependency-groups]
# Mirrors tests/requirements.txt for `uv sync`; pip users install that file.
dev = [
@@ -131,10 +165,36 @@ dev = [
"ruff",
]
[tool.uv]
# The repo is run in place (`application.*` imported from the checkout), not
# installed as a distribution.
package = false
[tool.hatch.version]
path = "docsgpt/version.py"
# The wheel is the docsgpt package with the data it reads at runtime: prompts,
# model catalogs, the seed config, alembic.ini and the migrations. Build inputs
# (Dockerfile, exported requirements), the sample index and local runtime data
# stay out. The one-release `application` import alias is checkout-only.
[tool.hatch.build.targets.wheel]
packages = ["docsgpt"]
exclude = [
"docsgpt/Dockerfile",
"docsgpt/requirements*.txt",
"docsgpt/index.faiss",
"docsgpt/index.pkl",
"docsgpt/indexes/",
"docsgpt/inputs/",
"docsgpt/vectors/",
]
[tool.hatch.build.targets.sdist]
include = ["/docsgpt"]
exclude = [
"docsgpt/Dockerfile",
"docsgpt/requirements*.txt",
"docsgpt/index.faiss",
"docsgpt/index.pkl",
"docsgpt/indexes/",
"docsgpt/inputs/",
"docsgpt/vectors/",
]
[[tool.uv.index]]
name = "pytorch-cpu"