# Conflicts: # .vite-hooks/pre-commit # api/controllers/console/__init__.py # api/core/agent/base_agent_runner.py # api/core/app/app_config/easy_ui_based_app/model_config/converter.py # api/core/app/apps/agent_chat/app_runner.py # api/core/entities/provider_configuration.py # api/core/helper/moderation.py # api/core/model_manager.py # api/core/rag/embedding/cached_embedding.py # api/core/rag/retrieval/dataset_retrieval.py # api/core/rag/splitter/fixed_text_splitter.py # api/core/workflow/nodes/datasource/datasource_node.py # api/core/workflow/nodes/knowledge_index/knowledge_index_node.py # api/models/human_input.py # api/providers/trace/trace-tencent/src/dify_trace_tencent/span_builder.py # api/services/workflow_service.py # api/tasks/trigger_processing_tasks.py # api/tests/integration_tests/core/workflow/nodes/datasource/test_datasource_node_integration.py # api/tests/integration_tests/workflow/nodes/test_http.py # api/tests/integration_tests/workflow/nodes/test_parameter_extractor.py # api/tests/unit_tests/controllers/service_api/app/test_conversation.py # api/tests/unit_tests/core/prompt/test_agent_history_prompt_transform.py # api/tests/unit_tests/core/variables/test_segment.py # api/tests/unit_tests/core/workflow/graph_engine/test_mock_factory.py # api/tests/unit_tests/core/workflow/nodes/answer/test_answer.py # api/tests/unit_tests/core/workflow/nodes/datasource/test_datasource_node.py # api/tests/unit_tests/core/workflow/nodes/http_request/test_http_request_node.py # api/tests/unit_tests/core/workflow/nodes/human_input/test_email_delivery_config.py # api/tests/unit_tests/services/workflow/test_workflow_human_input_delivery.py # web/app/(commonLayout)/layout.tsx # web/app/components/app/configuration/dataset-config/params-config/weighted-score.tsx # web/app/components/app/configuration/debug/debug-with-multiple-model/debug-item.tsx # web/app/components/app/workflow-log/__tests__/list.spec.tsx # web/app/components/apps/__tests__/list.spec.tsx # web/app/components/apps/list.tsx # web/app/components/base/chat/chat-with-history/header/operation.tsx # web/app/components/base/chat/chat-with-history/sidebar/operation.tsx # web/app/components/header/account-setting/data-source-page-new/operator.tsx # web/app/components/header/account-setting/members-page/operation/index.tsx # web/app/components/plugins/marketplace/sort-dropdown/__tests__/index.spec.tsx # web/app/components/plugins/marketplace/sort-dropdown/index.tsx # web/app/components/plugins/plugin-page/plugin-tasks/index.tsx # web/app/components/workflow/header/__tests__/test-run-menu.spec.tsx # web/app/components/workflow/header/test-run-menu.tsx # web/app/components/workflow/nodes/_base/components/next-step/operator.tsx # web/app/components/workflow/nodes/_base/components/panel-operator/index.tsx # web/app/components/workflow/nodes/assigner/components/__tests__/operation-selector.spec.tsx # web/app/components/workflow/nodes/assigner/components/operation-selector.tsx # web/app/components/workflow/operator/__tests__/more-actions.spec.tsx # web/app/components/workflow/operator/zoom-in-out.tsx # web/app/components/workflow/panel/version-history-panel/context-menu/menu-item.tsx # web/app/components/workflow/selection-contextmenu.tsx # web/eslint-suppressions.json Co-authored-by: FFXN <31929997+FFXN@users.noreply.github.com> |
||
|---|---|---|
| .. | ||
| trace-aliyun | ||
| trace-arize-phoenix | ||
| trace-langfuse | ||
| trace-langsmith | ||
| trace-mlflow | ||
| trace-opik | ||
| trace-tencent | ||
| trace-weave | ||
| README.md | ||
Trace providers
This directory holds optional workspace packages that send Dify ops tracing data (workflows, messages, tools, moderation, etc.) to an external observability backend (Langfuse, LangSmith, OpenTelemetry-style exporters, and others).
Unlike VDB providers, trace plugins are not discovered via entry points. The API core imports your package explicitly from core/ops/ops_trace_manager.py after you register the provider id and mapping.
Architecture
| Layer | Location | Role |
|---|---|---|
| Contracts | api/core/ops/base_trace_instance.py, api/core/ops/entities/trace_entity.py, api/core/ops/entities/config_entity.py |
BaseTraceInstance, BaseTracingConfig, and typed *TraceInfo payloads |
| Registry | api/core/ops/ops_trace_manager.py |
TracingProviderEnum, OpsTraceProviderConfigMap — maps provider string → config class, encrypted keys, and trace class |
| Your package | api/providers/trace/trace-<name>/ |
Pydantic config + subclass of BaseTraceInstance |
At runtime, OpsTraceManager decrypts stored credentials, builds your config model, caches a trace instance, and calls trace(trace_info) with a concrete BaseTraceInfo subtype.
What you implement
1. Config model (BaseTracingConfig)
Subclass BaseTracingConfig from core.ops.entities.config_entity. Use Pydantic validators; reuse helpers from core.ops.utils (for example validate_url, validate_url_with_path, validate_project_name) where appropriate.
Fields fall into two groups used by the manager:
secret_keys— names of fields that are encrypted at rest (API keys, tokens, passwords).other_keys— non-secret connection settings (hosts, project names, endpoints).
List these key names in your OpsTraceProviderConfigMap entry so encrypt/decrypt and merge logic stay correct.
2. Trace instance (BaseTraceInstance)
Subclass BaseTraceInstance and implement:
def trace(self, trace_info: BaseTraceInfo) -> None:
...
Dispatch on the concrete type with isinstance (see trace_langfuse or trace_langsmith for full patterns). Payload types are defined in core/ops/entities/trace_entity.py, including:
WorkflowTraceInfo,WorkflowNodeTraceInfo,DraftNodeExecutionTraceMessageTraceInfo,ToolTraceInfo,ModerationTraceInfo,SuggestedQuestionTraceInfoDatasetRetrievalTraceInfo,GenerateNameTraceInfo,PromptGenerationTraceInfo
You may ignore categories your backend does not support; existing providers often no-op unhandled types.
Optional: use get_service_account_with_tenant(app_id) from the base class when you need tenant-scoped account context.
3. Register in the API core
Upstream changes are required so Dify knows your provider exists:
TracingProviderEnum(api/core/ops/entities/config_entity.py) — add a new member whose value is the stable string stored in app tracing config (e.g."mybackend").OpsTraceProviderConfigMap.__getitem__(api/core/ops/ops_trace_manager.py) — add amatchcase for that enum member returning:config_class: your Pydantic config typesecret_keys/other_keys: lists of field names as abovetrace_instance: yourBaseTraceInstancesubclass
Lazy-import your package inside the case so missing optional installs raise a clearImportError.
If the match case is missing, the provider string will not resolve and tracing will be disabled for that app.
Package layout
Each provider is a normal uv workspace member, for example:
api/providers/trace/trace-<name>/pyproject.toml— project namedify-trace-<name>, dependencies on vendor SDKsapi/providers/trace/trace-<name>/src/dify_trace_<name>/—config.py,<name>_trace.py, optionalentities/, and an emptypy.typedfile (PEP 561) so the API type checker treats the package as typed; listpy.typedunder[tool.setuptools.package-data]for that import name inpyproject.toml.
Reference implementations: trace-langfuse/, trace-langsmith/, trace-opik/.
Wiring into the api workspace
In api/pyproject.toml:
[tool.uv.sources]—dify-trace-<name> = { workspace = true }[dependency-groups]— addtrace-<name> = ["dify-trace-<name>"]and includedify-trace-<name>intrace-allif it should ship with the default bundle
After changing metadata, run uv sync from api/.