mirror of
https://github.com/langgenius/dify.git
synced 2026-11-13 06:41:07 +08:00
221 lines
6.5 KiB
Python
221 lines
6.5 KiB
Python
from __future__ import annotations
|
|
|
|
from collections.abc import Mapping
|
|
from dataclasses import dataclass
|
|
from datetime import datetime
|
|
from typing import Any, NotRequired, Protocol, TypedDict, runtime_checkable
|
|
|
|
from dify_trace_sdk.events import DraftNodeExecuted, PromptGenerated, TracePayload, json_safe
|
|
|
|
from enterprise.telemetry.contracts import SignalType, TelemetryCase
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Context shapes
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TraceContext(TypedDict):
|
|
"""Common ``context`` shape for TRACE-routed cases."""
|
|
|
|
tenant_id: str | None
|
|
user_id: NotRequired[str | None]
|
|
app_id: NotRequired[str | None]
|
|
|
|
|
|
class MetricLogContext(TypedDict):
|
|
"""Common ``context`` shape for METRIC_LOG-routed cases."""
|
|
|
|
tenant_id: str | None
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Payload shapes
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class NodeExecutionPayload(TypedDict):
|
|
"""``payload`` shape for ``TelemetryCase.NODE_EXECUTION`` and ``DRAFT_NODE_EXECUTION``."""
|
|
|
|
node_execution_data: dict[str, Any]
|
|
|
|
|
|
class AppCreatedPayload(TypedDict):
|
|
"""``payload`` shape for ``TelemetryCase.APP_CREATED``."""
|
|
|
|
app_id: str | None
|
|
mode: NotRequired[str | None]
|
|
|
|
|
|
class AppUpdatedPayload(TypedDict):
|
|
"""``payload`` shape for ``TelemetryCase.APP_UPDATED``."""
|
|
|
|
app_id: str | None
|
|
|
|
|
|
class AppDeletedPayload(TypedDict):
|
|
"""``payload`` shape for ``TelemetryCase.APP_DELETED``."""
|
|
|
|
app_id: str | None
|
|
|
|
|
|
class PromptGenerationPayload(TypedDict):
|
|
"""``payload`` shape for ``TelemetryCase.PROMPT_GENERATION``."""
|
|
|
|
tenant_id: str
|
|
app_id: NotRequired[str | None]
|
|
operation_type: str
|
|
instruction: str
|
|
generated_output: str
|
|
model_provider: str
|
|
model_name: str
|
|
prompt_tokens: int
|
|
completion_tokens: int
|
|
total_tokens: int
|
|
latency: float
|
|
total_price: float | None
|
|
currency: str | None
|
|
error: str | None
|
|
started_at: datetime
|
|
ended_at: datetime
|
|
|
|
|
|
class FeedbackCreatedPayload(TypedDict):
|
|
"""``payload`` shape for ``TelemetryCase.FEEDBACK_CREATED``."""
|
|
|
|
message_id: str
|
|
app_id: str | None
|
|
conversation_id: NotRequired[str | None]
|
|
from_end_user_id: NotRequired[str | None]
|
|
from_account_id: NotRequired[str | None]
|
|
rating: NotRequired[str | None]
|
|
from_source: NotRequired[str | None]
|
|
content: NotRequired[str | None]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Shared context dataclass
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class TelemetryContext:
|
|
tenant_id: str | None = None
|
|
user_id: str | None = None
|
|
app_id: str | None = None
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Event protocol — every concrete event must declare these routing fields
|
|
# so the gateway can dispatch without external mapping tables.
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@runtime_checkable
|
|
class TelemetryEvent(Protocol):
|
|
"""Structural contract for all telemetry events.
|
|
|
|
All attributes are declared as read-only ``@property`` so that
|
|
frozen dataclasses satisfy the protocol (a bare attribute
|
|
annotation would require writability).
|
|
"""
|
|
|
|
@property
|
|
def context(self) -> TelemetryContext: ...
|
|
@property
|
|
def payload(self) -> Mapping[str, Any]: ...
|
|
@property
|
|
def case(self) -> TelemetryCase: ...
|
|
@property
|
|
def signal_type(self) -> SignalType: ...
|
|
@property
|
|
def ce_eligible(self) -> bool: ...
|
|
|
|
|
|
@runtime_checkable
|
|
class TraceSignalEvent(TelemetryEvent, Protocol):
|
|
"""A ``SignalType.TRACE`` event, delivered through the trace pipeline."""
|
|
|
|
def trace_payload(self) -> TracePayload: ...
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Concrete event classes
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class DraftNodeExecutionTraceEvent:
|
|
context: TelemetryContext
|
|
payload: NodeExecutionPayload
|
|
case: TelemetryCase = TelemetryCase.DRAFT_NODE_EXECUTION
|
|
signal_type: SignalType = SignalType.TRACE
|
|
ce_eligible: bool = False
|
|
|
|
def trace_payload(self) -> TracePayload:
|
|
node_execution = self.payload["node_execution_data"]
|
|
return DraftNodeExecuted(node_execution={key: json_safe(value) for key, value in node_execution.items()})
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class PromptGenerationEvent:
|
|
context: TelemetryContext
|
|
payload: PromptGenerationPayload
|
|
case: TelemetryCase = TelemetryCase.PROMPT_GENERATION
|
|
signal_type: SignalType = SignalType.TRACE
|
|
ce_eligible: bool = False
|
|
|
|
def trace_payload(self) -> TracePayload:
|
|
payload = self.payload
|
|
return PromptGenerated(
|
|
operation_type=payload["operation_type"],
|
|
instruction=payload["instruction"],
|
|
generated_output=payload["generated_output"],
|
|
model_provider=payload["model_provider"],
|
|
model_name=payload["model_name"],
|
|
prompt_tokens=payload["prompt_tokens"],
|
|
completion_tokens=payload["completion_tokens"],
|
|
total_tokens=payload["total_tokens"],
|
|
latency=payload["latency"],
|
|
total_price=payload["total_price"],
|
|
currency=payload["currency"],
|
|
error=payload["error"],
|
|
started_at=payload["started_at"],
|
|
ended_at=payload["ended_at"],
|
|
)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class AppCreatedEvent:
|
|
context: TelemetryContext
|
|
payload: AppCreatedPayload
|
|
case: TelemetryCase = TelemetryCase.APP_CREATED
|
|
signal_type: SignalType = SignalType.METRIC_LOG
|
|
ce_eligible: bool = False
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class AppUpdatedEvent:
|
|
context: TelemetryContext
|
|
payload: AppUpdatedPayload
|
|
case: TelemetryCase = TelemetryCase.APP_UPDATED
|
|
signal_type: SignalType = SignalType.METRIC_LOG
|
|
ce_eligible: bool = False
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class AppDeletedEvent:
|
|
context: TelemetryContext
|
|
payload: AppDeletedPayload
|
|
case: TelemetryCase = TelemetryCase.APP_DELETED
|
|
signal_type: SignalType = SignalType.METRIC_LOG
|
|
ce_eligible: bool = False
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class FeedbackCreatedEvent:
|
|
context: TelemetryContext
|
|
payload: FeedbackCreatedPayload
|
|
case: TelemetryCase = TelemetryCase.FEEDBACK_CREATED
|
|
signal_type: SignalType = SignalType.METRIC_LOG
|
|
ce_eligible: bool = False
|