mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-19 04:15:32 +00:00
refactor: read settings directly instead of getattr with a second default
About 85 call sites read a setting as getattr(settings, "NAME", fallback), each carrying its own copy of the default. Every one of those names is a field with a default on the model, so the fallback could never apply to the real settings object; it only masked drift. Two had drifted: - OPENAI_PROMPT_CACHE_KEY defaults to True on the model but the reader fell back to False, and two test stubs relied on that. - SharePoint's MICROSOFT_AUTHORITY fallback to https://login.microsoftonline.com/<tenant> never fired, because the attribute always exists (as None), so MSAL got authority=None. The connector now derives the tenant authority when the setting is unset, as its test always assumed. Four places read EMBEDDINGS_KEY straight from os.environ, skipping the "None"/"" normalisation the model applies; they read the setting now. Test stubs that replaced a module's settings with a SimpleNamespace list every setting the code under test reads.
This commit is contained in:
1 parent
d70644743b
commit
f882ef49a7
46 files changed
+111
-113
No files matched your search
@@ -382,7 +382,7 @@ class BaseAgent(ABC):
|
||||
when the conversation was compressed after that turn was produced —
|
||||
the compressed local history is the context then, not the server's.
|
||||
"""
|
||||
if not getattr(settings, "OPENAI_RESPONSES_CHAIN_ACROSS_TURNS", True):
|
||||
if not settings.OPENAI_RESPONSES_CHAIN_ACROSS_TURNS:
|
||||
return None
|
||||
if not self.chat_history:
|
||||
return None
|
||||
@@ -422,7 +422,7 @@ class BaseAgent(ABC):
|
||||
# No provider-reported usage on the previous turn (older rows,
|
||||
# estimate-only providers): nothing to bound against.
|
||||
return meta["response_id"]
|
||||
budget = getattr(settings, "OPENAI_RESPONSES_CHAIN_BUDGET_TOKENS", None)
|
||||
budget = settings.OPENAI_RESPONSES_CHAIN_BUDGET_TOKENS
|
||||
if not budget:
|
||||
from docsgpt.core.model_utils import get_token_limit
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ def _dedupable_tool_names() -> frozenset:
|
||||
"""
|
||||
from docsgpt.core.settings import settings
|
||||
|
||||
return frozenset(BUILTIN_AGENT_TOOLS) | frozenset(getattr(settings, "DEFAULT_CHAT_TOOLS", None) or [])
|
||||
return frozenset(BUILTIN_AGENT_TOOLS) | frozenset(settings.DEFAULT_CHAT_TOOLS or [])
|
||||
|
||||
|
||||
def _requires_approval(tool: Dict, action: Dict) -> bool:
|
||||
|
||||
@@ -829,7 +829,7 @@ class ArtifactGeneratorTool(Tool):
|
||||
spec_path = f"{token_dir}/spec.json"
|
||||
out_path = f"{token_dir}/out.{_KIND_INFO[kind]['ext']}"
|
||||
program = _RENDERERS[kind].format(spec_path=spec_path, out_path=out_path)
|
||||
timeout = float(getattr(settings, "SANDBOX_EXEC_TIMEOUT", 60))
|
||||
timeout = float(settings.SANDBOX_EXEC_TIMEOUT)
|
||||
|
||||
manager = SandboxCreator.get_manager()
|
||||
try:
|
||||
|
||||
@@ -113,7 +113,7 @@ def bridge_attachment(
|
||||
# Reject oversize attachments BEFORE buffering them: the authoritative ``size``
|
||||
# column lets us avoid pulling a multi-hundred-MB file fully into worker memory,
|
||||
# and the bounded read below backstops a missing/lying ``size``.
|
||||
max_bytes = int(getattr(settings, "ARTIFACT_MAX_BYTES", 0) or 0)
|
||||
max_bytes = int(settings.ARTIFACT_MAX_BYTES or 0)
|
||||
declared_size = attachment.get("size")
|
||||
if max_bytes and isinstance(declared_size, (int, float)) and declared_size > max_bytes:
|
||||
raise AttachmentBridgeError(
|
||||
|
||||
@@ -87,9 +87,9 @@ class CodeExecutorTool(Tool):
|
||||
baked in. Keep the package lists in sync with deployment/sandbox/Dockerfile
|
||||
(jupyter) and scripts/build_daytona_snapshot.py (daytona snapshot).
|
||||
"""
|
||||
backend = str(getattr(settings, "SANDBOX_BACKEND", "jupyter") or "jupyter").lower()
|
||||
backend = str(settings.SANDBOX_BACKEND or "jupyter").lower()
|
||||
if backend == "daytona":
|
||||
if getattr(settings, "DAYTONA_SNAPSHOT", None):
|
||||
if settings.DAYTONA_SNAPSHOT:
|
||||
return (
|
||||
"Preinstalled beyond the stdlib: python-pptx, python-docx, openpyxl, "
|
||||
"reportlab, lxml, pillow. pip install anything else from within the code "
|
||||
@@ -330,7 +330,7 @@ class CodeExecutorTool(Tool):
|
||||
# Reject an oversize input BEFORE buffering it: the declared ``size``
|
||||
# avoids pulling a huge file into worker memory, and the bounded read
|
||||
# below backstops a missing/lying size column.
|
||||
max_bytes = int(getattr(settings, "SANDBOX_MAX_INPUT_BYTES", 0) or 0)
|
||||
max_bytes = int(settings.SANDBOX_MAX_INPUT_BYTES or 0)
|
||||
declared_size = version.get("size")
|
||||
if max_bytes and isinstance(declared_size, (int, float)) and declared_size > max_bytes:
|
||||
return {"error": f"input artifact {artifact_id} exceeds the {max_bytes}-byte sandbox input limit."}
|
||||
@@ -484,7 +484,7 @@ class CodeExecutorTool(Tool):
|
||||
@staticmethod
|
||||
def _exec_timeout() -> float:
|
||||
"""Return the fixed per-run wall-clock cap (SANDBOX_EXEC_TIMEOUT; not caller-adjustable)."""
|
||||
return float(getattr(settings, "SANDBOX_EXEC_TIMEOUT", 60))
|
||||
return float(settings.SANDBOX_EXEC_TIMEOUT)
|
||||
|
||||
@staticmethod
|
||||
def _is_timeout(result: ExecResult) -> bool:
|
||||
|
||||
@@ -108,11 +108,11 @@ class MCPTool(Tool):
|
||||
if configured_redirect_uri:
|
||||
return configured_redirect_uri.rstrip("/")
|
||||
|
||||
explicit = getattr(settings, "MCP_OAUTH_REDIRECT_URI", None)
|
||||
explicit = settings.MCP_OAUTH_REDIRECT_URI
|
||||
if explicit:
|
||||
return explicit.rstrip("/")
|
||||
|
||||
connector_base = getattr(settings, "CONNECTOR_REDIRECT_BASE_URI", None)
|
||||
connector_base = settings.CONNECTOR_REDIRECT_BASE_URI
|
||||
if connector_base:
|
||||
parsed = urlparse(connector_base)
|
||||
if parsed.scheme and parsed.netloc:
|
||||
|
||||
@@ -255,7 +255,7 @@ class ReadDocumentTool(Tool):
|
||||
# The task's per-call time limits are raised to match the awaited window: bound to
|
||||
# the base timeout at import, the worker would otherwise self-terminate a large
|
||||
# parse long before this await gives up.
|
||||
queue = getattr(settings, "DOCUMENT_PARSE_QUEUE", "parsing")
|
||||
queue = settings.DOCUMENT_PARSE_QUEUE
|
||||
try:
|
||||
async_result = parse_document.apply_async(
|
||||
args=[artifact_id, parent, self.user_id, options],
|
||||
|
||||
@@ -331,7 +331,7 @@ class WorkflowAgent(BaseAgent):
|
||||
from docsgpt.storage.storage_creator import StorageCreator
|
||||
|
||||
storage = StorageCreator.get_storage()
|
||||
max_bytes = int(getattr(settings, "ARTIFACT_MAX_BYTES", 0) or 0)
|
||||
max_bytes = int(settings.ARTIFACT_MAX_BYTES or 0)
|
||||
dropped: List[str] = []
|
||||
if len(self.attachments) > _MAX_INPUT_DOCUMENTS:
|
||||
over = len(self.attachments) - _MAX_INPUT_DOCUMENTS
|
||||
|
||||
@@ -639,7 +639,7 @@ class WorkflowEngine:
|
||||
raw_ids = self._resolve_input_artifact_ids(inputs)
|
||||
if not raw_ids:
|
||||
return loaded
|
||||
max_bytes = int(getattr(settings, "SANDBOX_MAX_INPUT_BYTES", 0) or 0)
|
||||
max_bytes = int(settings.SANDBOX_MAX_INPUT_BYTES or 0)
|
||||
storage = StorageCreator.get_storage()
|
||||
# Two inputs whose current versions share a filename would clobber each other at the
|
||||
# same ``inputs/{name}`` path; track used paths and disambiguate deterministically.
|
||||
@@ -749,15 +749,15 @@ class WorkflowEngine:
|
||||
|
||||
supported = set(supported_types)
|
||||
supports_images = any(t.startswith("image/") for t in supported)
|
||||
max_files = int(getattr(settings, "WORKFLOW_NODE_NATIVE_MAX_FILES", 5))
|
||||
extract_max = int(getattr(settings, "WORKFLOW_NODE_EXTRACT_MAX_FILES", 5))
|
||||
max_files = int(settings.WORKFLOW_NODE_NATIVE_MAX_FILES)
|
||||
extract_max = int(settings.WORKFLOW_NODE_EXTRACT_MAX_FILES)
|
||||
# One wall clock for every blocking parse this node issues. The cap
|
||||
# above bounds how MANY parses run; this bounds how LONG they take in
|
||||
# total, so N documents cannot serialize N size-scaled windows.
|
||||
parse_deadline = time.monotonic() + float(
|
||||
getattr(settings, "WORKFLOW_NODE_EXTRACT_BUDGET_SECONDS", 900)
|
||||
settings.WORKFLOW_NODE_EXTRACT_BUDGET_SECONDS
|
||||
)
|
||||
max_bytes = int(getattr(settings, "SANDBOX_MAX_INPUT_BYTES", 25 * 1024 * 1024))
|
||||
max_bytes = int(settings.SANDBOX_MAX_INPUT_BYTES)
|
||||
|
||||
# One read-only connection for the whole batch; the resolved-version
|
||||
# rows are collected, then storage reads happen outside the DB context.
|
||||
@@ -976,7 +976,7 @@ class WorkflowEngine:
|
||||
if not user_id:
|
||||
return None
|
||||
options = {"output": "markdown", "include_tables": False, "persist": False}
|
||||
queue = getattr(settings, "DOCUMENT_PARSE_QUEUE", "parsing")
|
||||
queue = settings.DOCUMENT_PARSE_QUEUE
|
||||
# OCR cost scales with pages, so the window grows with the document's size
|
||||
# (floored at DOCUMENT_PARSE_TIMEOUT); the task's per-call time limits are
|
||||
# raised to match, else the worker would self-terminate mid-parse.
|
||||
@@ -1084,7 +1084,7 @@ class WorkflowEngine:
|
||||
"""Return the stricter of the node's requested timeout and the sandbox cap."""
|
||||
from docsgpt.core.settings import settings
|
||||
|
||||
cap = float(getattr(settings, "SANDBOX_EXEC_TIMEOUT", 60))
|
||||
cap = float(settings.SANDBOX_EXEC_TIMEOUT)
|
||||
if requested is None:
|
||||
return cap
|
||||
try:
|
||||
|
||||
Reference in new issue
Block a user