Ops Trace Data Models#
Overview#
Dify's ops trace system converts runtime events (messages, workflow runs) into structured trace objects for downstream observability providers. The pipeline is:
App invocation
β TraceTask (queued via TraceQueueManager)
β TraceTask.execute() β message_trace() / workflow_trace()
β MessageTraceInfo / WorkflowTraceInfo (Pydantic models)
β TaskData (serialized to storage)
β process_trace_tasks Celery task β tracing provider
Entity Definitions#
BaseTraceInfo β shared base#
Defined in api/core/ops/entities/trace_entity.py. All trace info models inherit from this Pydantic BaseModel. Key fields:
| Field | Type | Default | Notes |
|---|---|---|---|
operation_id | str | None | None | Unique identifier for this trace operation, automatically assigned if not provided |
message_id | str | None | N/A | ID of the related Message row |
metadata | dict[str, Any] | N/A | Carries app_id, tenant_id, and other context (see below) |
trace_id | str | None | N/A | External trace ID, e.g. from X-Trace-Id header |
app_id is not a top-level model field on either trace class β it lives inside metadata.
MessageTraceInfo#
Defined at lines 109β120. Adds LLM-specific fields: conversation_model, message_tokens, answer_tokens, total_tokens, error, file_list, message_file_data, conversation_mode, and streaming metrics.
WorkflowTraceInfo#
Defined at lines 87β106. Adds workflow-specific fields: workflow_id, tenant_id (top-level, required), workflow_run_id, workflow_run_elapsed_time, workflow_run_status, workflow_run_inputs/outputs/version, total_tokens, and optional conversation_id.
How app_id Flows Into Trace Metadata#
For MessageTraceInfo#
Constructed in message_trace():
- The
MessageORM row is fetched viaget_message_data(message_id). message_data.app_id(a non-nullableStringUUIDcolumn onMessage) is read directly .tenant_idis resolved by queryingApp.tenant_idwhereApp.id == message_data.app_id.- Both are placed into the
metadatadict at construction time :metadata["app_id"] = message_data.app_id.
For WorkflowTraceInfo#
Constructed in workflow_trace():
- The
WorkflowRunrow is fetched byworkflow_run_id. workflow_run.app_idis read directly (the field is required onWorkflowRun).- Placed into
metadata["app_id"]at construction .
TraceTask / TraceQueueManager#
TraceTask is initialized with self.app_id = None . When enqueued via TraceQueueManager.add_trace_task(), the manager's own self.app_id (set at construction from the call site) is assigned to trace_task.app_id. This app_id is then used as the storage key when serializing the TaskData to a file for Celery .
DB Entity Relationships#
The three source entities each carry app_id as a direct, non-nullable column:
| Entity | app_id column | Key relationships |
|---|---|---|
Message | StringUUID, nullable=False | conversation_id FK β Conversation; optional workflow_run_id |
Conversation | StringUUID, nullable=False | messages relationship β list of Message |
WorkflowRun | StringUUID, required | workflow_id; linked from Message.workflow_run_id |
Message carries a direct app_id column (not derived from Conversation), making trace construction efficient β no join is needed to resolve the app. The Message β Conversation link exists for conversation-mode lookups (e.g., fetching conversation_mode during message_trace()), not for app_id resolution .
Key Source Files#
| File | Purpose |
|---|---|
api/core/ops/entities/trace_entity.py | BaseTraceInfo, MessageTraceInfo, WorkflowTraceInfo Pydantic models |
api/core/ops/ops_trace_manager.py | TraceTask, TraceQueueManager, message_trace(), workflow_trace() |
api/models/model.py | Message, Conversation ORM models |