From f6269c483f55398d262d1daad5f1d38c77afc3b9 Mon Sep 17 00:00:00 2001 From: arc53-machine <232052973+arc53-machine@users.noreply.github.com> Date: Wed, 23 Sep 2026 17:14:51 +0100 Subject: [PATCH] Add execution-trace settings and declare opentelemetry-api TRACES_* settings for the per-request trace timeline and its GenAI OTel export. opentelemetry-api was only transitive; the tracing package imports it directly. --- docs/content/Deploying/Settings-Reference.mdx | 41 +++++++++++++++++ docsgpt/core/settings/__init__.py | 2 + docsgpt/core/settings/tracing.py | 45 +++++++++++++++++++ docsgpt/requirements-docling.txt | 1 + docsgpt/requirements-milvus.txt | 1 + docsgpt/requirements.txt | 1 + pyproject.toml | 2 + uv.lock | 2 + 8 files changed, 95 insertions(+) create mode 100644 docsgpt/core/settings/tracing.py diff --git a/docs/content/Deploying/Settings-Reference.mdx b/docs/content/Deploying/Settings-Reference.mdx index d9f59ec7..b08af68a 100644 --- a/docs/content/Deploying/Settings-Reference.mdx +++ b/docs/content/Deploying/Settings-Reference.mdx @@ -1451,6 +1451,47 @@ Type `int`, default `30`, must be `>= 1`. Days guardrail events are kept before the cleanup task removes them. +## Execution traces + +Recording of agent, LLM, tool and retrieval steps per request. + +### `TRACES_ENABLED` + +Type `bool`, default `true`. + +Record an execution trace (agent runs, LLM calls, tool calls, retrieval, embeddings) for every request, store it in request_traces and show it in the Logs UI. False records nothing. + +### `TRACES_CAPTURE_CONTENT` + +Type `bool`, default `true`. + +Store short, secret-redacted previews (tool arguments and results, retrieved chunk titles, rephrased queries, answer excerpts) with each stored trace. Full prompts are never stored. OTel export follows OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT instead. + +### `TRACES_PREVIEW_CHARS` + +Type `int`, default `2000`, must be `>= 100`. + +Maximum characters kept per stored trace preview. + +### `TRACES_MAX_SPANS` + +Type `int`, default `500`, must be `>= 10`. + +Maximum spans recorded per trace; further spans are counted as dropped, not stored. + +### `TRACES_RETENTION_DAYS` + +Type `int`, default `30`, must be `>= 1`. + +Days stored traces are kept before the cleanup task removes them. + +### `TRACES_OTEL_EXPORT` + +Type `bool`, default `true`. + +Also emit each finished trace as OpenTelemetry GenAI spans (gen_ai.*) and metrics. Has no effect unless an OTel SDK is configured, e.g. by launching under opentelemetry-instrument. + + ## Quotas Quota window and the treatment of unpriced models. diff --git a/docsgpt/core/settings/__init__.py b/docsgpt/core/settings/__init__.py index 817281c5..aab95515 100644 --- a/docsgpt/core/settings/__init__.py +++ b/docsgpt/core/settings/__init__.py @@ -35,6 +35,7 @@ from docsgpt.core.settings.scheduler import SchedulerSettings from docsgpt.core.settings.server import ServerSettings from docsgpt.core.settings.speech import SpeechSettings from docsgpt.core.settings.storage import StorageSettings +from docsgpt.core.settings.tracing import TracingSettings from docsgpt.core.settings.vectorstores import VectorStoreSettings from docsgpt.core.settings.workers import WorkerSettings @@ -55,6 +56,7 @@ SETTINGS_GROUPS: tuple[tuple[str, type[SettingsGroup]], ...] = ( ("Events and devices", EventsSettings), ("Agents", AgentSettings), ("Guardrails", GuardrailSettings), + ("Execution traces", TracingSettings), ("Quotas", QuotaSettings), ("Scheduler", SchedulerSettings), ("Sandbox", SandboxSettings), diff --git a/docsgpt/core/settings/tracing.py b/docsgpt/core/settings/tracing.py new file mode 100644 index 00000000..19a26341 --- /dev/null +++ b/docsgpt/core/settings/tracing.py @@ -0,0 +1,45 @@ +"""Execution traces: the per-request timeline shown in the Logs UI and exported as OTel GenAI spans.""" + +from __future__ import annotations + +from pydantic import Field + +from docsgpt.core.settings._shared import SettingsGroup + + +class TracingSettings(SettingsGroup): + """Recording of agent, LLM, tool and retrieval steps per request.""" + + TRACES_ENABLED: bool = Field( + default=True, + description=( + "Record an execution trace (agent runs, LLM calls, tool calls, retrieval, embeddings) for every " + "request, store it in request_traces and show it in the Logs UI. False records nothing." + ), + ) + TRACES_CAPTURE_CONTENT: bool = Field( + default=True, + description=( + "Store short, secret-redacted previews (tool arguments and results, retrieved chunk titles, " + "rephrased queries, answer excerpts) with each stored trace. Full prompts are never stored. OTel " + "export follows OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT instead." + ), + ) + TRACES_PREVIEW_CHARS: int = Field( + default=2000, ge=100, description="Maximum characters kept per stored trace preview." + ) + TRACES_MAX_SPANS: int = Field( + default=500, + ge=10, + description="Maximum spans recorded per trace; further spans are counted as dropped, not stored.", + ) + TRACES_RETENTION_DAYS: int = Field( + default=30, ge=1, description="Days stored traces are kept before the cleanup task removes them." + ) + TRACES_OTEL_EXPORT: bool = Field( + default=True, + description=( + "Also emit each finished trace as OpenTelemetry GenAI spans (gen_ai.*) and metrics. Has no effect " + "unless an OTel SDK is configured, e.g. by launching under opentelemetry-instrument." + ), + ) diff --git a/docsgpt/requirements-docling.txt b/docsgpt/requirements-docling.txt index 2e272a2e..a22caa19 100644 --- a/docsgpt/requirements-docling.txt +++ b/docsgpt/requirements-docling.txt @@ -519,6 +519,7 @@ openpyxl==3.1.5 opentelemetry-api==1.44.0 # via # daytona + # docsgpt # fastmcp-slim # google-api-core # mcp diff --git a/docsgpt/requirements-milvus.txt b/docsgpt/requirements-milvus.txt index 46d3d2b9..713ded5c 100644 --- a/docsgpt/requirements-milvus.txt +++ b/docsgpt/requirements-milvus.txt @@ -438,6 +438,7 @@ openpyxl==3.1.5 opentelemetry-api==1.44.0 # via # daytona + # docsgpt # fastmcp-slim # google-api-core # mcp diff --git a/docsgpt/requirements.txt b/docsgpt/requirements.txt index 17db5b1d..b4b45c8f 100644 --- a/docsgpt/requirements.txt +++ b/docsgpt/requirements.txt @@ -429,6 +429,7 @@ openpyxl==3.1.5 opentelemetry-api==1.44.0 # via # daytona + # docsgpt # fastmcp-slim # google-api-core # mcp diff --git a/pyproject.toml b/pyproject.toml index 5e24bbaa..dbbfc373 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -78,6 +78,8 @@ dependencies = [ "openapi3-parser>=2.0.0,<3", # pandas reads .xlsx through openpyxl but does not depend on it. "openpyxl>=3.1.5,<4", + # Imported directly by docsgpt.tracing to replay traces as GenAI spans. + "opentelemetry-api>=1.29.0,<2", "opentelemetry-distro>=0.50b0,<1", "opentelemetry-exporter-otlp>=1.29.0,<2", "opentelemetry-instrumentation-celery>=0.50b0,<1", diff --git a/uv.lock b/uv.lock index dba25614..13780640 100644 --- a/uv.lock +++ b/uv.lock @@ -1375,6 +1375,7 @@ dependencies = [ { name = "openai" }, { name = "openapi3-parser" }, { name = "openpyxl" }, + { name = "opentelemetry-api" }, { name = "opentelemetry-distro" }, { name = "opentelemetry-exporter-otlp" }, { name = "opentelemetry-instrumentation-celery" }, @@ -1488,6 +1489,7 @@ requires-dist = [ { name = "openai", specifier = ">=3.13.0,<4" }, { name = "openapi3-parser", specifier = ">=2.0.0,<3" }, { name = "openpyxl", specifier = ">=3.1.5,<4" }, + { name = "opentelemetry-api", specifier = ">=1.29.0,<2" }, { name = "opentelemetry-distro", specifier = ">=0.50b0,<1" }, { name = "opentelemetry-exporter-otlp", specifier = ">=1.29.0,<2" }, { name = "opentelemetry-instrumentation-celery", specifier = ">=0.50b0,<1" },