mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-14 12:16:20 +00:00
chore(deps): bump 47 backend dependencies
Bumps the Python backend dependency set, holding back the ones that are
resolver-blocked or that regress behaviour this repo depends on.
Notable upgrades:
cryptography 46.0.7 -> 50.0.0 (requires msal 1.37.0, which relaxes its cap)
protobuf 6.33.6 -> 7.35.1 (floats opentelemetry-* to 1.44.0)
openai 2.32.0 -> 2.53.0, anthropic 0.88.0 -> 0.121.0
google-genai 1.73.1 -> 2.17.0 (the 2.0 break is scoped to the Interactions
API, which this repo does not use)
fastmcp 3.2.4 -> 3.4.6, gunicorn 25.3.0 -> 26.0.0
starlette 1.0.0 -> 1.6.0, uvicorn 0.42.0 -> 0.52.1
sentence-transformers 5.3.0 -> 5.7.0, numpy 2.4.4 -> 2.5.1
faiss-cpu 1.13.2 -> 1.15.0, pillow -> 12.3.0 (pinned; security release)
pypdf 6.9.2 -> 6.15.0, lxml 6.0.2 -> 6.1.1 (CVE-2026-41066)
Code changes needed by the bumps:
- openai >= 2.53 rejects a falsy api_key at construction. Keyless
OpenAI-compatible backends (Ollama, llama.cpp, vLLM) legitimately have
none, and pydantic-settings yields "" for a bare `API_KEY=` in .env, so
both call sites now fall back to a placeholder.
- Flask >= 3.1.2 tears a stream_with_context request down twice, and a
ContextVar token may only be reset once. The log-context teardown hook
is now idempotent.
- sentence-transformers renamed get_sentence_embedding_dimension to
get_embedding_dimension in 5.4; use the new name with a fallback.
Held back deliberately:
torch 2.11.0 - 2.13.0 drags torchvision 0.26 -> 0.28 and the whole
docling stack; torch is only probed for cuda.empty_cache()
tokenizers 0.22.2 - transformers pins <=0.23.0 and no stable 0.23.0 exists
transformers - capped <5.9.0: 5.9+ breaks docling's PDF layout model on
Apple Silicon (MPS float64), matching docling-core's own
darwin pin
docling 2.84.0 - 2.118.1 needs rapidocr >=3.9.1 and conflicts with
transformers on macOS; wants its own PR with a
golden-corpus diff
redis 7.4.0 - 8.x defaults socket_timeout to 5s, silently capping the
blocking reads in the device broker and SSE tail
websockets 16.0 - google-genai caps <17.0
marshmallow - dataclasses-json hard-caps <4; spec tightened to match
langchain block - langchain-community 0.4.2 deletes the Qdrant vectorstore
this repo imports; langchain 1.3.x needs websockets <16
This commit is contained in:
1 parent
732f7d1a89
commit
2565906f88
9 files changed
+148
-50
No files matched your search
@@ -183,6 +183,10 @@ def _reset_log_context(_exc):
|
||||
# leak into the stream's view of the context.
|
||||
token = getattr(request, _LOG_CTX_TOKEN_ATTR, None)
|
||||
if token is not None:
|
||||
# Flask >= 3.1.2 tears a stream_with_context request down twice: once
|
||||
# when the view returns, once when the generator is finalized. Clear
|
||||
# the token first — resetting one twice raises RuntimeError.
|
||||
setattr(request, _LOG_CTX_TOKEN_ATTR, None)
|
||||
log_context.reset(token)
|
||||
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@ from application.core.settings import settings
|
||||
from application.llm.base import BaseLLM
|
||||
from application.storage.storage_creator import StorageCreator
|
||||
|
||||
# Placeholder sent to OpenAI-compatible backends that require no credentials.
|
||||
NO_API_KEY = "sk-no-key"
|
||||
|
||||
|
||||
def _truncate_base64_for_logging(messages):
|
||||
"""
|
||||
@@ -124,7 +127,12 @@ class OpenAILLM(BaseLLM):
|
||||
):
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
self.api_key = api_key or settings.OPENAI_API_KEY or settings.API_KEY
|
||||
# openai>=2.53 rejects a falsy api_key at construction. Keyless
|
||||
# OpenAI-compatible backends (Ollama, llama.cpp, vLLM) legitimately have
|
||||
# none, and pydantic-settings yields "" for a bare `API_KEY=` in .env.
|
||||
self.api_key = (
|
||||
api_key or settings.OPENAI_API_KEY or settings.API_KEY or NO_API_KEY
|
||||
)
|
||||
self.user_api_key = user_api_key
|
||||
|
||||
# Priority: 1) Parameter base_url, 2) Settings OPENAI_BASE_URL, 3) Default
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
a2wsgi==1.10.10
|
||||
alembic>=1.13,<2
|
||||
anthropic==0.88.0
|
||||
anthropic==0.121.0
|
||||
asgiref>=3.11.1
|
||||
boto3==1.42.83
|
||||
beautifulsoup4==4.14.3
|
||||
boto3==1.43.67
|
||||
beautifulsoup4==4.15.0
|
||||
cel-python==0.5.0
|
||||
celery==5.6.3
|
||||
celery-redbeat==2.3.3
|
||||
croniter==6.2.2
|
||||
cryptography==46.0.7
|
||||
celery-redbeat==2.4.2
|
||||
croniter==6.2.4
|
||||
cryptography==50.0.0
|
||||
dataclasses-json==0.6.7
|
||||
daytona==0.190.1
|
||||
daytona==0.203.0
|
||||
defusedxml==0.7.1
|
||||
docling==2.84.0
|
||||
rapidocr>=1.4.0
|
||||
@@ -18,19 +18,19 @@ onnxruntime>=1.19.0
|
||||
docx2txt==0.9
|
||||
ddgs>=8.0.0
|
||||
fast-ebook
|
||||
elevenlabs==2.43.0
|
||||
Flask==3.1.1
|
||||
faiss-cpu==1.13.2
|
||||
fastmcp==3.2.4
|
||||
elevenlabs==2.62.0
|
||||
Flask==3.1.3
|
||||
faiss-cpu==1.15.0
|
||||
fastmcp==3.4.6
|
||||
flask-restx==1.3.2
|
||||
google-genai==1.73.1
|
||||
google-api-python-client==2.193.0
|
||||
google-auth-httplib2==0.3.1
|
||||
google-auth-oauthlib==1.3.1
|
||||
google-genai==2.17.0
|
||||
google-api-python-client==2.198.0
|
||||
google-auth-httplib2==0.4.1
|
||||
google-auth-oauthlib==1.4.0
|
||||
gTTS==2.5.4
|
||||
gunicorn==25.3.0
|
||||
gunicorn==26.0.0
|
||||
jinja2==3.1.6
|
||||
jiter==0.13.0
|
||||
jiter==0.16.0
|
||||
jmespath==1.1.0
|
||||
joblib==1.5.3
|
||||
jsonpatch==1.33
|
||||
@@ -38,21 +38,22 @@ jsonpointer==3.1.1
|
||||
kombu==5.6.2
|
||||
langchain==1.2.3
|
||||
langchain-community==0.4.1
|
||||
langchain-core==1.2.29
|
||||
langchain-core==1.2.31
|
||||
langchain-openai==1.1.12
|
||||
langchain-text-splitters==1.1.1
|
||||
langchain-text-splitters==1.1.2
|
||||
langsmith==0.7.31
|
||||
lazy-object-proxy==1.12.0
|
||||
lxml==6.0.2
|
||||
lxml==6.1.1
|
||||
markupsafe==3.0.3
|
||||
marshmallow>=3.18.0,<5.0.0
|
||||
# dataclasses-json (via langchain-community) hard-caps marshmallow <4.
|
||||
marshmallow>=3.18.0,<4.0.0
|
||||
mpmath==1.3.0
|
||||
multidict==6.7.1
|
||||
msal==1.35.1
|
||||
msal==1.37.0
|
||||
mypy-extensions==1.1.0
|
||||
networkx==3.6.1
|
||||
numpy==2.4.4
|
||||
openai==2.32.0
|
||||
numpy==2.5.1
|
||||
openai==2.53.0
|
||||
openapi3-parser==1.1.22
|
||||
opentelemetry-distro>=0.50b0,<1
|
||||
opentelemetry-exporter-otlp>=1.29.0,<2
|
||||
@@ -64,23 +65,23 @@ opentelemetry-instrumentation-redis>=0.50b0,<1
|
||||
opentelemetry-instrumentation-requests>=0.50b0,<1
|
||||
opentelemetry-instrumentation-sqlalchemy>=0.50b0,<1
|
||||
opentelemetry-instrumentation-starlette>=0.50b0,<1
|
||||
orjson==3.11.7
|
||||
packaging==26.0
|
||||
pandas==3.0.2
|
||||
orjson==3.11.9
|
||||
packaging==26.3
|
||||
pandas==3.0.5
|
||||
openpyxl==3.1.5
|
||||
pathable==0.5.0
|
||||
pathable==0.6.0
|
||||
pdf2image>=1.17.0
|
||||
pgvector>=0.2,<1
|
||||
pillow
|
||||
pgvector>=0.5,<1
|
||||
pillow==12.3.0
|
||||
portalocker>=2.7.0,<4.0.0
|
||||
prompt-toolkit==3.0.52
|
||||
protobuf==6.33.6
|
||||
prompt-toolkit==3.0.53
|
||||
protobuf==7.35.1
|
||||
psycopg[binary,pool]>=3.1,<4
|
||||
py==1.11.0
|
||||
pydantic
|
||||
pydantic-core
|
||||
pydantic-settings
|
||||
pypdf==6.9.2
|
||||
pypdf==6.15.0
|
||||
python-dateutil==2.9.0.post0
|
||||
python-dotenv
|
||||
python-jose==3.5.0
|
||||
@@ -88,28 +89,31 @@ python-pptx==1.0.2
|
||||
PyYAML
|
||||
redis==7.4.0
|
||||
referencing>=0.28.0,<0.38.0
|
||||
regex==2026.4.4
|
||||
requests==2.33.1
|
||||
regex==2026.7.19
|
||||
requests==2.34.2
|
||||
retry==0.9.2
|
||||
sentence-transformers==5.3.0
|
||||
sentence-transformers==5.7.0
|
||||
sqlalchemy>=2.0,<3
|
||||
starlette>=1.0,<2
|
||||
tiktoken==0.12.0
|
||||
tiktoken==0.13.0
|
||||
tokenizers==0.22.2
|
||||
torch==2.11.0
|
||||
tqdm==4.67.3
|
||||
transformers==5.4.0
|
||||
typing-extensions==4.15.0
|
||||
# Capped <5.9.0 to match docling-core's own darwin pin: 5.9+ breaks docling's
|
||||
# PDF layout model on Apple Silicon ("Cannot convert a MPS Tensor to float64").
|
||||
transformers==5.8.1
|
||||
typing-extensions==4.16.0
|
||||
typing-inspect==0.9.0
|
||||
tzdata==2026.1
|
||||
urllib3==2.6.3
|
||||
tzdata==2026.3
|
||||
urllib3==2.7.0
|
||||
uvicorn[standard]>=0.30,<1
|
||||
uvicorn-worker>=0.4,<1
|
||||
vine==5.1.0
|
||||
wcwidth==0.6.0
|
||||
wcwidth==0.8.2
|
||||
werkzeug>=3.1.0
|
||||
yarl==1.23.0
|
||||
markdownify==1.2.2
|
||||
tldextract==5.3.1
|
||||
yarl==1.24.5
|
||||
markdownify==1.2.3
|
||||
tldextract==5.3.2
|
||||
# google-genai caps websockets <17.0; 17.x will not resolve.
|
||||
websockets==16.0
|
||||
websocket-client==1.9.0
|
||||
@@ -326,7 +326,7 @@ class DaytonaSandbox(CodeSandbox):
|
||||
|
||||
The tools pass a token DIRECTORY (e.g. ``artifacts/{token}``), so the delete is
|
||||
recursive — ``fs.delete_file`` with ``recursive=True`` removes a folder and its
|
||||
contents (daytona==0.190.1).
|
||||
contents (daytona==0.203.0).
|
||||
"""
|
||||
try:
|
||||
handle = self._get_handle(session_id)
|
||||
|
||||
@@ -6,6 +6,9 @@ from openai import OpenAI
|
||||
from application.core.settings import settings
|
||||
from application.stt.base import BaseSTT
|
||||
|
||||
# Placeholder sent to OpenAI-compatible backends that require no credentials.
|
||||
NO_API_KEY = "sk-no-key"
|
||||
|
||||
|
||||
class OpenAISTT(BaseSTT):
|
||||
def __init__(
|
||||
@@ -14,7 +17,10 @@ class OpenAISTT(BaseSTT):
|
||||
base_url: Optional[str] = None,
|
||||
model: Optional[str] = None,
|
||||
):
|
||||
self.api_key = api_key or settings.OPENAI_API_KEY or settings.API_KEY
|
||||
# See application/llm/openai.py: openai>=2.53 rejects a falsy api_key.
|
||||
self.api_key = (
|
||||
api_key or settings.OPENAI_API_KEY or settings.API_KEY or NO_API_KEY
|
||||
)
|
||||
self.base_url = base_url or settings.OPENAI_BASE_URL or "https://api.openai.com/v1"
|
||||
self.model = model or settings.OPENAI_STT_MODEL
|
||||
self.client = OpenAI(api_key=self.api_key, base_url=self.base_url)
|
||||
|
||||
@@ -24,7 +24,13 @@ class EmbeddingsWrapper:
|
||||
raise ValueError(
|
||||
f"SentenceTransformer model failed to load properly for: {model_name}"
|
||||
)
|
||||
self.dimension = self.model.get_sentence_embedding_dimension()
|
||||
# Renamed in sentence-transformers 5.4; keep the old name as a fallback.
|
||||
get_dimension = getattr(
|
||||
self.model,
|
||||
"get_embedding_dimension",
|
||||
getattr(self.model, "get_sentence_embedding_dimension", None),
|
||||
)
|
||||
self.dimension = get_dimension()
|
||||
logging.info(f"Successfully loaded model with dimension: {self.dimension}")
|
||||
except Exception as e:
|
||||
logging.error(
|
||||
|
||||
@@ -2049,3 +2049,34 @@ class TestInlineFilePartResolution:
|
||||
# Upload happened because the read errored → treated as miss.
|
||||
assert len(llm.client.files.calls) == 1
|
||||
assert out[0]["content"][1] == {"type": "file", "file": {"file_id": "file-1"}}
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestKeylessConstruction:
|
||||
"""openai>=2.53 raises on a falsy api_key at construction.
|
||||
|
||||
Keyless OpenAI-compatible backends (Ollama, llama.cpp, vLLM) have no
|
||||
credential, and pydantic-settings yields "" — not None — for a bare
|
||||
`API_KEY=` line in .env, so the empty string must not reach the SDK.
|
||||
"""
|
||||
|
||||
@pytest.mark.parametrize("blank", ["", None])
|
||||
def test_llm_accepts_blank_key(self, blank):
|
||||
with patch("application.llm.openai.settings") as mock_settings:
|
||||
mock_settings.OPENAI_API_KEY = blank
|
||||
mock_settings.API_KEY = blank
|
||||
mock_settings.OPENAI_BASE_URL = "http://localhost:11434/v1"
|
||||
llm = OpenAILLM(api_key=blank)
|
||||
assert llm.api_key == "sk-no-key"
|
||||
|
||||
@pytest.mark.parametrize("blank", ["", None])
|
||||
def test_stt_accepts_blank_key(self, blank):
|
||||
from application.stt.openai_stt import OpenAISTT
|
||||
|
||||
with patch("application.stt.openai_stt.settings") as mock_settings:
|
||||
mock_settings.OPENAI_API_KEY = blank
|
||||
mock_settings.API_KEY = blank
|
||||
mock_settings.OPENAI_BASE_URL = "http://localhost:11434/v1"
|
||||
mock_settings.OPENAI_STT_MODEL = "whisper-1"
|
||||
stt = OpenAISTT(api_key=blank)
|
||||
assert stt.api_key == "sk-no-key"
|
||||
@@ -19,3 +19,26 @@ def test_app_config():
|
||||
assert app.config["UPLOAD_FOLDER"] == "inputs"
|
||||
assert app.config["CELERY_BROKER_URL"] == settings.CELERY_BROKER_URL
|
||||
assert app.config["CELERY_RESULT_BACKEND"] == settings.CELERY_RESULT_BACKEND
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestLogContextTeardown:
|
||||
"""Flask >= 3.1.2 tears a stream_with_context request down twice: once when
|
||||
the view returns, once when the generator is finalized. A ContextVar token
|
||||
may only be reset once, so the teardown hook has to be idempotent."""
|
||||
|
||||
def test_reset_is_idempotent(self):
|
||||
from flask import request
|
||||
|
||||
from application.app import _LOG_CTX_TOKEN_ATTR, _reset_log_context, app
|
||||
from application.core import log_context
|
||||
|
||||
with app.test_request_context("/"):
|
||||
token = log_context.bind(activity_id="abc", endpoint="stream")
|
||||
setattr(request, _LOG_CTX_TOKEN_ATTR, token)
|
||||
|
||||
_reset_log_context(None)
|
||||
_reset_log_context(None) # second teardown must not raise
|
||||
|
||||
assert getattr(request, _LOG_CTX_TOKEN_ATTR) is None
|
||||
assert "activity_id" not in log_context.snapshot()
|
||||
@@ -9,7 +9,7 @@ class TestEmbeddingsWrapper:
|
||||
def test_init_success(self, mock_st_cls):
|
||||
mock_model = MagicMock()
|
||||
mock_model._first_module.return_value = MagicMock()
|
||||
mock_model.get_sentence_embedding_dimension.return_value = 768
|
||||
mock_model.get_embedding_dimension.return_value = 768
|
||||
mock_st_cls.return_value = mock_model
|
||||
|
||||
from application.vectorstore.embeddings_local import EmbeddingsWrapper
|
||||
@@ -19,6 +19,22 @@ class TestEmbeddingsWrapper:
|
||||
mock_st_cls.assert_called_once()
|
||||
assert wrapper.dimension == 768
|
||||
|
||||
@patch("application.vectorstore.embeddings_local.SentenceTransformer")
|
||||
def test_init_falls_back_to_legacy_dimension_api(self, mock_st_cls):
|
||||
"""sentence-transformers < 5.4 only exposes get_sentence_embedding_dimension."""
|
||||
mock_model = MagicMock()
|
||||
mock_model._first_module.return_value = MagicMock()
|
||||
del mock_model.get_embedding_dimension
|
||||
mock_model.get_sentence_embedding_dimension.return_value = 768
|
||||
mock_st_cls.return_value = mock_model
|
||||
|
||||
from application.vectorstore.embeddings_local import EmbeddingsWrapper
|
||||
|
||||
wrapper = EmbeddingsWrapper("test-model")
|
||||
|
||||
assert wrapper.dimension == 768
|
||||
mock_model.get_sentence_embedding_dimension.assert_called_once()
|
||||
|
||||
@patch("application.vectorstore.embeddings_local.SentenceTransformer")
|
||||
def test_init_failure(self, mock_st_cls):
|
||||
mock_st_cls.side_effect = Exception("model not found")
|
||||
|
||||
Reference in new issue
Block a user