feat: Add Aliyun LLM Observability Integration (#21471)
This commit is contained in:
0
api/core/ops/aliyun_trace/__init__.py
Normal file
0
api/core/ops/aliyun_trace/__init__.py
Normal file
486
api/core/ops/aliyun_trace/aliyun_trace.py
Normal file
486
api/core/ops/aliyun_trace/aliyun_trace.py
Normal file
@@ -0,0 +1,486 @@
|
|||||||
|
import json
|
||||||
|
import logging
|
||||||
|
from collections.abc import Sequence
|
||||||
|
from typing import Optional
|
||||||
|
from urllib.parse import urljoin
|
||||||
|
|
||||||
|
from opentelemetry.trace import Status, StatusCode
|
||||||
|
from sqlalchemy.orm import Session, sessionmaker
|
||||||
|
|
||||||
|
from core.ops.aliyun_trace.data_exporter.traceclient import (
|
||||||
|
TraceClient,
|
||||||
|
convert_datetime_to_nanoseconds,
|
||||||
|
convert_to_span_id,
|
||||||
|
convert_to_trace_id,
|
||||||
|
generate_span_id,
|
||||||
|
)
|
||||||
|
from core.ops.aliyun_trace.entities.aliyun_trace_entity import SpanData
|
||||||
|
from core.ops.aliyun_trace.entities.semconv import (
|
||||||
|
GEN_AI_COMPLETION,
|
||||||
|
GEN_AI_FRAMEWORK,
|
||||||
|
GEN_AI_MODEL_NAME,
|
||||||
|
GEN_AI_PROMPT,
|
||||||
|
GEN_AI_PROMPT_TEMPLATE_TEMPLATE,
|
||||||
|
GEN_AI_PROMPT_TEMPLATE_VARIABLE,
|
||||||
|
GEN_AI_RESPONSE_FINISH_REASON,
|
||||||
|
GEN_AI_SESSION_ID,
|
||||||
|
GEN_AI_SPAN_KIND,
|
||||||
|
GEN_AI_SYSTEM,
|
||||||
|
GEN_AI_USAGE_INPUT_TOKENS,
|
||||||
|
GEN_AI_USAGE_OUTPUT_TOKENS,
|
||||||
|
GEN_AI_USAGE_TOTAL_TOKENS,
|
||||||
|
GEN_AI_USER_ID,
|
||||||
|
INPUT_VALUE,
|
||||||
|
OUTPUT_VALUE,
|
||||||
|
RETRIEVAL_DOCUMENT,
|
||||||
|
RETRIEVAL_QUERY,
|
||||||
|
TOOL_DESCRIPTION,
|
||||||
|
TOOL_NAME,
|
||||||
|
TOOL_PARAMETERS,
|
||||||
|
GenAISpanKind,
|
||||||
|
)
|
||||||
|
from core.ops.base_trace_instance import BaseTraceInstance
|
||||||
|
from core.ops.entities.config_entity import AliyunConfig
|
||||||
|
from core.ops.entities.trace_entity import (
|
||||||
|
BaseTraceInfo,
|
||||||
|
DatasetRetrievalTraceInfo,
|
||||||
|
GenerateNameTraceInfo,
|
||||||
|
MessageTraceInfo,
|
||||||
|
ModerationTraceInfo,
|
||||||
|
SuggestedQuestionTraceInfo,
|
||||||
|
ToolTraceInfo,
|
||||||
|
WorkflowTraceInfo,
|
||||||
|
)
|
||||||
|
from core.rag.models.document import Document
|
||||||
|
from core.repositories import SQLAlchemyWorkflowNodeExecutionRepository
|
||||||
|
from core.workflow.entities.workflow_node_execution import (
|
||||||
|
WorkflowNodeExecution,
|
||||||
|
WorkflowNodeExecutionMetadataKey,
|
||||||
|
WorkflowNodeExecutionStatus,
|
||||||
|
)
|
||||||
|
from core.workflow.nodes import NodeType
|
||||||
|
from models import Account, App, EndUser, TenantAccountJoin, WorkflowNodeExecutionTriggeredFrom, db
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class AliyunDataTrace(BaseTraceInstance):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
aliyun_config: AliyunConfig,
|
||||||
|
):
|
||||||
|
super().__init__(aliyun_config)
|
||||||
|
base_url = aliyun_config.endpoint.rstrip("/")
|
||||||
|
endpoint = urljoin(base_url, f"adapt_{aliyun_config.license_key}/api/otlp/traces")
|
||||||
|
self.trace_client = TraceClient(service_name=aliyun_config.app_name, endpoint=endpoint)
|
||||||
|
|
||||||
|
def trace(self, trace_info: BaseTraceInfo):
|
||||||
|
if isinstance(trace_info, WorkflowTraceInfo):
|
||||||
|
self.workflow_trace(trace_info)
|
||||||
|
if isinstance(trace_info, MessageTraceInfo):
|
||||||
|
self.message_trace(trace_info)
|
||||||
|
if isinstance(trace_info, ModerationTraceInfo):
|
||||||
|
pass
|
||||||
|
if isinstance(trace_info, SuggestedQuestionTraceInfo):
|
||||||
|
self.suggested_question_trace(trace_info)
|
||||||
|
if isinstance(trace_info, DatasetRetrievalTraceInfo):
|
||||||
|
self.dataset_retrieval_trace(trace_info)
|
||||||
|
if isinstance(trace_info, ToolTraceInfo):
|
||||||
|
self.tool_trace(trace_info)
|
||||||
|
if isinstance(trace_info, GenerateNameTraceInfo):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def api_check(self):
|
||||||
|
return self.trace_client.api_check()
|
||||||
|
|
||||||
|
def get_project_url(self):
|
||||||
|
try:
|
||||||
|
return self.trace_client.get_project_url()
|
||||||
|
except Exception as e:
|
||||||
|
logger.info(f"Aliyun get run url failed: {str(e)}", exc_info=True)
|
||||||
|
raise ValueError(f"Aliyun get run url failed: {str(e)}")
|
||||||
|
|
||||||
|
def workflow_trace(self, trace_info: WorkflowTraceInfo):
|
||||||
|
trace_id = convert_to_trace_id(trace_info.workflow_run_id)
|
||||||
|
workflow_span_id = convert_to_span_id(trace_info.workflow_run_id, "workflow")
|
||||||
|
self.add_workflow_span(trace_id, workflow_span_id, trace_info)
|
||||||
|
|
||||||
|
workflow_node_executions = self.get_workflow_node_executions(trace_info)
|
||||||
|
for node_execution in workflow_node_executions:
|
||||||
|
node_span = self.build_workflow_node_span(node_execution, trace_id, trace_info, workflow_span_id)
|
||||||
|
self.trace_client.add_span(node_span)
|
||||||
|
|
||||||
|
def message_trace(self, trace_info: MessageTraceInfo):
|
||||||
|
message_data = trace_info.message_data
|
||||||
|
if message_data is None:
|
||||||
|
return
|
||||||
|
message_id = trace_info.message_id
|
||||||
|
|
||||||
|
user_id = message_data.from_account_id
|
||||||
|
if message_data.from_end_user_id:
|
||||||
|
end_user_data: Optional[EndUser] = (
|
||||||
|
db.session.query(EndUser).filter(EndUser.id == message_data.from_end_user_id).first()
|
||||||
|
)
|
||||||
|
if end_user_data is not None:
|
||||||
|
user_id = end_user_data.session_id
|
||||||
|
|
||||||
|
status: Status = Status(StatusCode.OK)
|
||||||
|
if trace_info.error:
|
||||||
|
status = Status(StatusCode.ERROR, trace_info.error)
|
||||||
|
|
||||||
|
trace_id = convert_to_trace_id(message_id)
|
||||||
|
message_span_id = convert_to_span_id(message_id, "message")
|
||||||
|
message_span = SpanData(
|
||||||
|
trace_id=trace_id,
|
||||||
|
parent_span_id=None,
|
||||||
|
span_id=message_span_id,
|
||||||
|
name="message",
|
||||||
|
start_time=convert_datetime_to_nanoseconds(trace_info.start_time),
|
||||||
|
end_time=convert_datetime_to_nanoseconds(trace_info.end_time),
|
||||||
|
attributes={
|
||||||
|
GEN_AI_SESSION_ID: trace_info.metadata.get("conversation_id", ""),
|
||||||
|
GEN_AI_USER_ID: str(user_id),
|
||||||
|
GEN_AI_SPAN_KIND: GenAISpanKind.CHAIN.value,
|
||||||
|
GEN_AI_FRAMEWORK: "dify",
|
||||||
|
INPUT_VALUE: json.dumps(trace_info.inputs, ensure_ascii=False),
|
||||||
|
OUTPUT_VALUE: str(trace_info.outputs),
|
||||||
|
},
|
||||||
|
status=status,
|
||||||
|
)
|
||||||
|
self.trace_client.add_span(message_span)
|
||||||
|
|
||||||
|
app_model_config = getattr(trace_info.message_data, "app_model_config", {})
|
||||||
|
pre_prompt = getattr(app_model_config, "pre_prompt", "")
|
||||||
|
inputs_data = getattr(trace_info.message_data, "inputs", {})
|
||||||
|
llm_span = SpanData(
|
||||||
|
trace_id=trace_id,
|
||||||
|
parent_span_id=message_span_id,
|
||||||
|
span_id=convert_to_span_id(message_id, "llm"),
|
||||||
|
name="llm",
|
||||||
|
start_time=convert_datetime_to_nanoseconds(trace_info.start_time),
|
||||||
|
end_time=convert_datetime_to_nanoseconds(trace_info.end_time),
|
||||||
|
attributes={
|
||||||
|
GEN_AI_SESSION_ID: trace_info.metadata.get("conversation_id", ""),
|
||||||
|
GEN_AI_USER_ID: str(user_id),
|
||||||
|
GEN_AI_SPAN_KIND: GenAISpanKind.LLM.value,
|
||||||
|
GEN_AI_FRAMEWORK: "dify",
|
||||||
|
GEN_AI_MODEL_NAME: trace_info.metadata.get("ls_model_name", ""),
|
||||||
|
GEN_AI_SYSTEM: trace_info.metadata.get("ls_provider", ""),
|
||||||
|
GEN_AI_USAGE_INPUT_TOKENS: str(trace_info.message_tokens),
|
||||||
|
GEN_AI_USAGE_OUTPUT_TOKENS: str(trace_info.answer_tokens),
|
||||||
|
GEN_AI_USAGE_TOTAL_TOKENS: str(trace_info.total_tokens),
|
||||||
|
GEN_AI_PROMPT_TEMPLATE_VARIABLE: json.dumps(inputs_data, ensure_ascii=False),
|
||||||
|
GEN_AI_PROMPT_TEMPLATE_TEMPLATE: pre_prompt,
|
||||||
|
GEN_AI_PROMPT: json.dumps(trace_info.inputs, ensure_ascii=False),
|
||||||
|
GEN_AI_COMPLETION: str(trace_info.outputs),
|
||||||
|
INPUT_VALUE: json.dumps(trace_info.inputs, ensure_ascii=False),
|
||||||
|
OUTPUT_VALUE: str(trace_info.outputs),
|
||||||
|
},
|
||||||
|
status=status,
|
||||||
|
)
|
||||||
|
self.trace_client.add_span(llm_span)
|
||||||
|
|
||||||
|
def dataset_retrieval_trace(self, trace_info: DatasetRetrievalTraceInfo):
|
||||||
|
if trace_info.message_data is None:
|
||||||
|
return
|
||||||
|
message_id = trace_info.message_id
|
||||||
|
|
||||||
|
documents_data = extract_retrieval_documents(trace_info.documents)
|
||||||
|
dataset_retrieval_span = SpanData(
|
||||||
|
trace_id=convert_to_trace_id(message_id),
|
||||||
|
parent_span_id=convert_to_span_id(message_id, "message"),
|
||||||
|
span_id=generate_span_id(),
|
||||||
|
name="dataset_retrieval",
|
||||||
|
start_time=convert_datetime_to_nanoseconds(trace_info.start_time),
|
||||||
|
end_time=convert_datetime_to_nanoseconds(trace_info.end_time),
|
||||||
|
attributes={
|
||||||
|
GEN_AI_SPAN_KIND: GenAISpanKind.RETRIEVER.value,
|
||||||
|
GEN_AI_FRAMEWORK: "dify",
|
||||||
|
RETRIEVAL_QUERY: str(trace_info.inputs),
|
||||||
|
RETRIEVAL_DOCUMENT: json.dumps(documents_data, ensure_ascii=False),
|
||||||
|
INPUT_VALUE: str(trace_info.inputs),
|
||||||
|
OUTPUT_VALUE: json.dumps(documents_data, ensure_ascii=False),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
self.trace_client.add_span(dataset_retrieval_span)
|
||||||
|
|
||||||
|
def tool_trace(self, trace_info: ToolTraceInfo):
|
||||||
|
if trace_info.message_data is None:
|
||||||
|
return
|
||||||
|
message_id = trace_info.message_id
|
||||||
|
|
||||||
|
status: Status = Status(StatusCode.OK)
|
||||||
|
if trace_info.error:
|
||||||
|
status = Status(StatusCode.ERROR, trace_info.error)
|
||||||
|
|
||||||
|
tool_span = SpanData(
|
||||||
|
trace_id=convert_to_trace_id(message_id),
|
||||||
|
parent_span_id=convert_to_span_id(message_id, "message"),
|
||||||
|
span_id=generate_span_id(),
|
||||||
|
name=trace_info.tool_name,
|
||||||
|
start_time=convert_datetime_to_nanoseconds(trace_info.start_time),
|
||||||
|
end_time=convert_datetime_to_nanoseconds(trace_info.end_time),
|
||||||
|
attributes={
|
||||||
|
GEN_AI_SPAN_KIND: GenAISpanKind.TOOL.value,
|
||||||
|
GEN_AI_FRAMEWORK: "dify",
|
||||||
|
TOOL_NAME: trace_info.tool_name,
|
||||||
|
TOOL_DESCRIPTION: json.dumps(trace_info.tool_config, ensure_ascii=False),
|
||||||
|
TOOL_PARAMETERS: json.dumps(trace_info.tool_inputs, ensure_ascii=False),
|
||||||
|
INPUT_VALUE: json.dumps(trace_info.inputs, ensure_ascii=False),
|
||||||
|
OUTPUT_VALUE: str(trace_info.tool_outputs),
|
||||||
|
},
|
||||||
|
status=status,
|
||||||
|
)
|
||||||
|
self.trace_client.add_span(tool_span)
|
||||||
|
|
||||||
|
def get_workflow_node_executions(self, trace_info: WorkflowTraceInfo) -> Sequence[WorkflowNodeExecution]:
|
||||||
|
# through workflow_run_id get all_nodes_execution using repository
|
||||||
|
session_factory = sessionmaker(bind=db.engine)
|
||||||
|
# Find the app's creator account
|
||||||
|
with Session(db.engine, expire_on_commit=False) as session:
|
||||||
|
# Get the app to find its creator
|
||||||
|
app_id = trace_info.metadata.get("app_id")
|
||||||
|
if not app_id:
|
||||||
|
raise ValueError("No app_id found in trace_info metadata")
|
||||||
|
|
||||||
|
app = session.query(App).filter(App.id == app_id).first()
|
||||||
|
if not app:
|
||||||
|
raise ValueError(f"App with id {app_id} not found")
|
||||||
|
|
||||||
|
if not app.created_by:
|
||||||
|
raise ValueError(f"App with id {app_id} has no creator (created_by is None)")
|
||||||
|
|
||||||
|
service_account = session.query(Account).filter(Account.id == app.created_by).first()
|
||||||
|
if not service_account:
|
||||||
|
raise ValueError(f"Creator account with id {app.created_by} not found for app {app_id}")
|
||||||
|
current_tenant = (
|
||||||
|
session.query(TenantAccountJoin).filter_by(account_id=service_account.id, current=True).first()
|
||||||
|
)
|
||||||
|
if not current_tenant:
|
||||||
|
raise ValueError(f"Current tenant not found for account {service_account.id}")
|
||||||
|
service_account.set_tenant_id(current_tenant.tenant_id)
|
||||||
|
workflow_node_execution_repository = SQLAlchemyWorkflowNodeExecutionRepository(
|
||||||
|
session_factory=session_factory,
|
||||||
|
user=service_account,
|
||||||
|
app_id=trace_info.metadata.get("app_id"),
|
||||||
|
triggered_from=WorkflowNodeExecutionTriggeredFrom.WORKFLOW_RUN,
|
||||||
|
)
|
||||||
|
# Get all executions for this workflow run
|
||||||
|
workflow_node_executions = workflow_node_execution_repository.get_by_workflow_run(
|
||||||
|
workflow_run_id=trace_info.workflow_run_id
|
||||||
|
)
|
||||||
|
return workflow_node_executions
|
||||||
|
|
||||||
|
def build_workflow_node_span(
|
||||||
|
self, node_execution: WorkflowNodeExecution, trace_id: int, trace_info: WorkflowTraceInfo, workflow_span_id: int
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
if node_execution.node_type == NodeType.LLM:
|
||||||
|
node_span = self.build_workflow_llm_span(trace_id, workflow_span_id, trace_info, node_execution)
|
||||||
|
elif node_execution.node_type == NodeType.KNOWLEDGE_RETRIEVAL:
|
||||||
|
node_span = self.build_workflow_retrieval_span(trace_id, workflow_span_id, trace_info, node_execution)
|
||||||
|
elif node_execution.node_type == NodeType.TOOL:
|
||||||
|
node_span = self.build_workflow_tool_span(trace_id, workflow_span_id, trace_info, node_execution)
|
||||||
|
else:
|
||||||
|
node_span = self.build_workflow_task_span(trace_id, workflow_span_id, trace_info, node_execution)
|
||||||
|
return node_span
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_workflow_node_status(self, node_execution: WorkflowNodeExecution) -> Status:
|
||||||
|
span_status: Status = Status(StatusCode.UNSET)
|
||||||
|
if node_execution.status == WorkflowNodeExecutionStatus.SUCCEEDED:
|
||||||
|
span_status = Status(StatusCode.OK)
|
||||||
|
elif node_execution.status in [WorkflowNodeExecutionStatus.FAILED, WorkflowNodeExecutionStatus.EXCEPTION]:
|
||||||
|
span_status = Status(StatusCode.ERROR, str(node_execution.error))
|
||||||
|
return span_status
|
||||||
|
|
||||||
|
def build_workflow_task_span(
|
||||||
|
self, trace_id: int, workflow_span_id: int, trace_info: WorkflowTraceInfo, node_execution: WorkflowNodeExecution
|
||||||
|
) -> SpanData:
|
||||||
|
return SpanData(
|
||||||
|
trace_id=trace_id,
|
||||||
|
parent_span_id=workflow_span_id,
|
||||||
|
span_id=convert_to_span_id(node_execution.id, "node"),
|
||||||
|
name=node_execution.title,
|
||||||
|
start_time=convert_datetime_to_nanoseconds(node_execution.created_at),
|
||||||
|
end_time=convert_datetime_to_nanoseconds(node_execution.finished_at),
|
||||||
|
attributes={
|
||||||
|
GEN_AI_SESSION_ID: trace_info.metadata.get("conversation_id", ""),
|
||||||
|
GEN_AI_SPAN_KIND: GenAISpanKind.TASK.value,
|
||||||
|
GEN_AI_FRAMEWORK: "dify",
|
||||||
|
INPUT_VALUE: json.dumps(node_execution.inputs, ensure_ascii=False),
|
||||||
|
OUTPUT_VALUE: json.dumps(node_execution.outputs, ensure_ascii=False),
|
||||||
|
},
|
||||||
|
status=self.get_workflow_node_status(node_execution),
|
||||||
|
)
|
||||||
|
|
||||||
|
def build_workflow_tool_span(
|
||||||
|
self, trace_id: int, workflow_span_id: int, trace_info: WorkflowTraceInfo, node_execution: WorkflowNodeExecution
|
||||||
|
) -> SpanData:
|
||||||
|
tool_des = {}
|
||||||
|
if node_execution.metadata:
|
||||||
|
tool_des = node_execution.metadata.get(WorkflowNodeExecutionMetadataKey.TOOL_INFO, {})
|
||||||
|
return SpanData(
|
||||||
|
trace_id=trace_id,
|
||||||
|
parent_span_id=workflow_span_id,
|
||||||
|
span_id=convert_to_span_id(node_execution.id, "node"),
|
||||||
|
name=node_execution.title,
|
||||||
|
start_time=convert_datetime_to_nanoseconds(node_execution.created_at),
|
||||||
|
end_time=convert_datetime_to_nanoseconds(node_execution.finished_at),
|
||||||
|
attributes={
|
||||||
|
GEN_AI_SPAN_KIND: GenAISpanKind.TOOL.value,
|
||||||
|
GEN_AI_FRAMEWORK: "dify",
|
||||||
|
TOOL_NAME: node_execution.title,
|
||||||
|
TOOL_DESCRIPTION: json.dumps(tool_des, ensure_ascii=False),
|
||||||
|
TOOL_PARAMETERS: json.dumps(node_execution.inputs if node_execution.inputs else {}, ensure_ascii=False),
|
||||||
|
INPUT_VALUE: json.dumps(node_execution.inputs if node_execution.inputs else {}, ensure_ascii=False),
|
||||||
|
OUTPUT_VALUE: json.dumps(node_execution.outputs, ensure_ascii=False),
|
||||||
|
},
|
||||||
|
status=self.get_workflow_node_status(node_execution),
|
||||||
|
)
|
||||||
|
|
||||||
|
def build_workflow_retrieval_span(
|
||||||
|
self, trace_id: int, workflow_span_id: int, trace_info: WorkflowTraceInfo, node_execution: WorkflowNodeExecution
|
||||||
|
) -> SpanData:
|
||||||
|
input_value = ""
|
||||||
|
if node_execution.inputs:
|
||||||
|
input_value = str(node_execution.inputs.get("query", ""))
|
||||||
|
output_value = ""
|
||||||
|
if node_execution.outputs:
|
||||||
|
output_value = json.dumps(node_execution.outputs.get("result", []), ensure_ascii=False)
|
||||||
|
return SpanData(
|
||||||
|
trace_id=trace_id,
|
||||||
|
parent_span_id=workflow_span_id,
|
||||||
|
span_id=convert_to_span_id(node_execution.id, "node"),
|
||||||
|
name=node_execution.title,
|
||||||
|
start_time=convert_datetime_to_nanoseconds(node_execution.created_at),
|
||||||
|
end_time=convert_datetime_to_nanoseconds(node_execution.finished_at),
|
||||||
|
attributes={
|
||||||
|
GEN_AI_SPAN_KIND: GenAISpanKind.RETRIEVER.value,
|
||||||
|
GEN_AI_FRAMEWORK: "dify",
|
||||||
|
RETRIEVAL_QUERY: input_value,
|
||||||
|
RETRIEVAL_DOCUMENT: output_value,
|
||||||
|
INPUT_VALUE: input_value,
|
||||||
|
OUTPUT_VALUE: output_value,
|
||||||
|
},
|
||||||
|
status=self.get_workflow_node_status(node_execution),
|
||||||
|
)
|
||||||
|
|
||||||
|
def build_workflow_llm_span(
|
||||||
|
self, trace_id: int, workflow_span_id: int, trace_info: WorkflowTraceInfo, node_execution: WorkflowNodeExecution
|
||||||
|
) -> SpanData:
|
||||||
|
process_data = node_execution.process_data or {}
|
||||||
|
outputs = node_execution.outputs or {}
|
||||||
|
return SpanData(
|
||||||
|
trace_id=trace_id,
|
||||||
|
parent_span_id=workflow_span_id,
|
||||||
|
span_id=convert_to_span_id(node_execution.id, "node"),
|
||||||
|
name=node_execution.title,
|
||||||
|
start_time=convert_datetime_to_nanoseconds(node_execution.created_at),
|
||||||
|
end_time=convert_datetime_to_nanoseconds(node_execution.finished_at),
|
||||||
|
attributes={
|
||||||
|
GEN_AI_SESSION_ID: trace_info.metadata.get("conversation_id", ""),
|
||||||
|
GEN_AI_SPAN_KIND: GenAISpanKind.LLM.value,
|
||||||
|
GEN_AI_FRAMEWORK: "dify",
|
||||||
|
GEN_AI_MODEL_NAME: process_data.get("model_name", ""),
|
||||||
|
GEN_AI_SYSTEM: process_data.get("model_provider", ""),
|
||||||
|
GEN_AI_USAGE_INPUT_TOKENS: str(outputs.get("usage", {}).get("prompt_tokens", 0)),
|
||||||
|
GEN_AI_USAGE_OUTPUT_TOKENS: str(outputs.get("usage", {}).get("completion_tokens", 0)),
|
||||||
|
GEN_AI_USAGE_TOTAL_TOKENS: str(outputs.get("usage", {}).get("total_tokens", 0)),
|
||||||
|
GEN_AI_PROMPT: json.dumps(process_data.get("prompts", []), ensure_ascii=False),
|
||||||
|
GEN_AI_COMPLETION: str(outputs.get("text", "")),
|
||||||
|
GEN_AI_RESPONSE_FINISH_REASON: outputs.get("finish_reason", ""),
|
||||||
|
INPUT_VALUE: json.dumps(process_data.get("prompts", []), ensure_ascii=False),
|
||||||
|
OUTPUT_VALUE: str(outputs.get("text", "")),
|
||||||
|
},
|
||||||
|
status=self.get_workflow_node_status(node_execution),
|
||||||
|
)
|
||||||
|
|
||||||
|
def add_workflow_span(self, trace_id: int, workflow_span_id: int, trace_info: WorkflowTraceInfo):
|
||||||
|
message_span_id = None
|
||||||
|
if trace_info.message_id:
|
||||||
|
message_span_id = convert_to_span_id(trace_info.message_id, "message")
|
||||||
|
user_id = trace_info.metadata.get("user_id")
|
||||||
|
status: Status = Status(StatusCode.OK)
|
||||||
|
if trace_info.error:
|
||||||
|
status = Status(StatusCode.ERROR, trace_info.error)
|
||||||
|
if message_span_id: # chatflow
|
||||||
|
message_span = SpanData(
|
||||||
|
trace_id=trace_id,
|
||||||
|
parent_span_id=None,
|
||||||
|
span_id=message_span_id,
|
||||||
|
name="message",
|
||||||
|
start_time=convert_datetime_to_nanoseconds(trace_info.start_time),
|
||||||
|
end_time=convert_datetime_to_nanoseconds(trace_info.end_time),
|
||||||
|
attributes={
|
||||||
|
GEN_AI_SESSION_ID: trace_info.metadata.get("conversation_id", ""),
|
||||||
|
GEN_AI_USER_ID: str(user_id),
|
||||||
|
GEN_AI_SPAN_KIND: GenAISpanKind.CHAIN.value,
|
||||||
|
GEN_AI_FRAMEWORK: "dify",
|
||||||
|
INPUT_VALUE: trace_info.workflow_run_inputs.get("sys.query", ""),
|
||||||
|
OUTPUT_VALUE: json.dumps(trace_info.workflow_run_outputs, ensure_ascii=False),
|
||||||
|
},
|
||||||
|
status=status,
|
||||||
|
)
|
||||||
|
self.trace_client.add_span(message_span)
|
||||||
|
|
||||||
|
workflow_span = SpanData(
|
||||||
|
trace_id=trace_id,
|
||||||
|
parent_span_id=message_span_id,
|
||||||
|
span_id=workflow_span_id,
|
||||||
|
name="workflow",
|
||||||
|
start_time=convert_datetime_to_nanoseconds(trace_info.start_time),
|
||||||
|
end_time=convert_datetime_to_nanoseconds(trace_info.end_time),
|
||||||
|
attributes={
|
||||||
|
GEN_AI_USER_ID: str(user_id),
|
||||||
|
GEN_AI_SPAN_KIND: GenAISpanKind.CHAIN.value,
|
||||||
|
GEN_AI_FRAMEWORK: "dify",
|
||||||
|
INPUT_VALUE: json.dumps(trace_info.workflow_run_inputs, ensure_ascii=False),
|
||||||
|
OUTPUT_VALUE: json.dumps(trace_info.workflow_run_outputs, ensure_ascii=False),
|
||||||
|
},
|
||||||
|
status=status,
|
||||||
|
)
|
||||||
|
self.trace_client.add_span(workflow_span)
|
||||||
|
|
||||||
|
def suggested_question_trace(self, trace_info: SuggestedQuestionTraceInfo):
|
||||||
|
message_id = trace_info.message_id
|
||||||
|
status: Status = Status(StatusCode.OK)
|
||||||
|
if trace_info.error:
|
||||||
|
status = Status(StatusCode.ERROR, trace_info.error)
|
||||||
|
suggested_question_span = SpanData(
|
||||||
|
trace_id=convert_to_trace_id(message_id),
|
||||||
|
parent_span_id=convert_to_span_id(message_id, "message"),
|
||||||
|
span_id=convert_to_span_id(message_id, "suggested_question"),
|
||||||
|
name="suggested_question",
|
||||||
|
start_time=convert_datetime_to_nanoseconds(trace_info.start_time),
|
||||||
|
end_time=convert_datetime_to_nanoseconds(trace_info.end_time),
|
||||||
|
attributes={
|
||||||
|
GEN_AI_SPAN_KIND: GenAISpanKind.LLM.value,
|
||||||
|
GEN_AI_FRAMEWORK: "dify",
|
||||||
|
GEN_AI_MODEL_NAME: trace_info.metadata.get("ls_model_name", ""),
|
||||||
|
GEN_AI_SYSTEM: trace_info.metadata.get("ls_provider", ""),
|
||||||
|
GEN_AI_PROMPT: json.dumps(trace_info.inputs, ensure_ascii=False),
|
||||||
|
GEN_AI_COMPLETION: json.dumps(trace_info.suggested_question, ensure_ascii=False),
|
||||||
|
INPUT_VALUE: json.dumps(trace_info.inputs, ensure_ascii=False),
|
||||||
|
OUTPUT_VALUE: json.dumps(trace_info.suggested_question, ensure_ascii=False),
|
||||||
|
},
|
||||||
|
status=status,
|
||||||
|
)
|
||||||
|
self.trace_client.add_span(suggested_question_span)
|
||||||
|
|
||||||
|
|
||||||
|
def extract_retrieval_documents(documents: list[Document]):
|
||||||
|
documents_data = []
|
||||||
|
for document in documents:
|
||||||
|
document_data = {
|
||||||
|
"content": document.page_content,
|
||||||
|
"metadata": {
|
||||||
|
"dataset_id": document.metadata.get("dataset_id"),
|
||||||
|
"doc_id": document.metadata.get("doc_id"),
|
||||||
|
"document_id": document.metadata.get("document_id"),
|
||||||
|
},
|
||||||
|
"score": document.metadata.get("score"),
|
||||||
|
}
|
||||||
|
documents_data.append(document_data)
|
||||||
|
return documents_data
|
0
api/core/ops/aliyun_trace/data_exporter/__init__.py
Normal file
0
api/core/ops/aliyun_trace/data_exporter/__init__.py
Normal file
200
api/core/ops/aliyun_trace/data_exporter/traceclient.py
Normal file
200
api/core/ops/aliyun_trace/data_exporter/traceclient.py
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
import hashlib
|
||||||
|
import logging
|
||||||
|
import random
|
||||||
|
import socket
|
||||||
|
import threading
|
||||||
|
import uuid
|
||||||
|
from collections import deque
|
||||||
|
from collections.abc import Sequence
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from opentelemetry import trace as trace_api
|
||||||
|
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
||||||
|
from opentelemetry.sdk.resources import Resource
|
||||||
|
from opentelemetry.sdk.trace import ReadableSpan
|
||||||
|
from opentelemetry.sdk.util.instrumentation import InstrumentationScope
|
||||||
|
from opentelemetry.semconv.resource import ResourceAttributes
|
||||||
|
|
||||||
|
from configs import dify_config
|
||||||
|
from core.ops.aliyun_trace.entities.aliyun_trace_entity import SpanData
|
||||||
|
|
||||||
|
INVALID_SPAN_ID = 0x0000000000000000
|
||||||
|
INVALID_TRACE_ID = 0x00000000000000000000000000000000
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class TraceClient:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
service_name: str,
|
||||||
|
endpoint: str,
|
||||||
|
max_queue_size: int = 1000,
|
||||||
|
schedule_delay_sec: int = 5,
|
||||||
|
max_export_batch_size: int = 50,
|
||||||
|
):
|
||||||
|
self.endpoint = endpoint
|
||||||
|
self.resource = Resource(
|
||||||
|
attributes={
|
||||||
|
ResourceAttributes.SERVICE_NAME: service_name,
|
||||||
|
ResourceAttributes.SERVICE_VERSION: f"dify-{dify_config.project.version}-{dify_config.COMMIT_SHA}",
|
||||||
|
ResourceAttributes.DEPLOYMENT_ENVIRONMENT: f"{dify_config.DEPLOY_ENV}-{dify_config.EDITION}",
|
||||||
|
ResourceAttributes.HOST_NAME: socket.gethostname(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.span_builder = SpanBuilder(self.resource)
|
||||||
|
self.exporter = OTLPSpanExporter(endpoint=endpoint)
|
||||||
|
|
||||||
|
self.max_queue_size = max_queue_size
|
||||||
|
self.schedule_delay_sec = schedule_delay_sec
|
||||||
|
self.max_export_batch_size = max_export_batch_size
|
||||||
|
|
||||||
|
self.queue: deque = deque(maxlen=max_queue_size)
|
||||||
|
self.condition = threading.Condition(threading.Lock())
|
||||||
|
self.done = False
|
||||||
|
|
||||||
|
self.worker_thread = threading.Thread(target=self._worker, daemon=True)
|
||||||
|
self.worker_thread.start()
|
||||||
|
|
||||||
|
self._spans_dropped = False
|
||||||
|
|
||||||
|
def export(self, spans: Sequence[ReadableSpan]):
|
||||||
|
self.exporter.export(spans)
|
||||||
|
|
||||||
|
def api_check(self):
|
||||||
|
try:
|
||||||
|
response = requests.head(self.endpoint, timeout=5)
|
||||||
|
if response.status_code == 405:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.debug(f"AliyunTrace API check failed: Unexpected status code: {response.status_code}")
|
||||||
|
return False
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
logger.debug(f"AliyunTrace API check failed: {str(e)}")
|
||||||
|
raise ValueError(f"AliyunTrace API check failed: {str(e)}")
|
||||||
|
|
||||||
|
def get_project_url(self):
|
||||||
|
return "https://arms.console.aliyun.com/#/llm"
|
||||||
|
|
||||||
|
def add_span(self, span_data: SpanData):
|
||||||
|
if span_data is None:
|
||||||
|
return
|
||||||
|
span: ReadableSpan = self.span_builder.build_span(span_data)
|
||||||
|
with self.condition:
|
||||||
|
if len(self.queue) == self.max_queue_size:
|
||||||
|
if not self._spans_dropped:
|
||||||
|
logger.warning("Queue is full, likely spans will be dropped.")
|
||||||
|
self._spans_dropped = True
|
||||||
|
|
||||||
|
self.queue.appendleft(span)
|
||||||
|
if len(self.queue) >= self.max_export_batch_size:
|
||||||
|
self.condition.notify()
|
||||||
|
|
||||||
|
def _worker(self):
|
||||||
|
while not self.done:
|
||||||
|
with self.condition:
|
||||||
|
if len(self.queue) < self.max_export_batch_size and not self.done:
|
||||||
|
self.condition.wait(timeout=self.schedule_delay_sec)
|
||||||
|
self._export_batch()
|
||||||
|
|
||||||
|
def _export_batch(self):
|
||||||
|
spans_to_export: list[ReadableSpan] = []
|
||||||
|
with self.condition:
|
||||||
|
while len(spans_to_export) < self.max_export_batch_size and self.queue:
|
||||||
|
spans_to_export.append(self.queue.pop())
|
||||||
|
|
||||||
|
if spans_to_export:
|
||||||
|
try:
|
||||||
|
self.exporter.export(spans_to_export)
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"Error exporting spans: {e}")
|
||||||
|
|
||||||
|
def shutdown(self):
|
||||||
|
with self.condition:
|
||||||
|
self.done = True
|
||||||
|
self.condition.notify_all()
|
||||||
|
self.worker_thread.join()
|
||||||
|
self._export_batch()
|
||||||
|
self.exporter.shutdown()
|
||||||
|
|
||||||
|
|
||||||
|
class SpanBuilder:
|
||||||
|
def __init__(self, resource):
|
||||||
|
self.resource = resource
|
||||||
|
self.instrumentation_scope = InstrumentationScope(
|
||||||
|
__name__,
|
||||||
|
"",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
|
def build_span(self, span_data: SpanData) -> ReadableSpan:
|
||||||
|
span_context = trace_api.SpanContext(
|
||||||
|
trace_id=span_data.trace_id,
|
||||||
|
span_id=span_data.span_id,
|
||||||
|
is_remote=False,
|
||||||
|
trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED),
|
||||||
|
trace_state=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
parent_span_context = None
|
||||||
|
if span_data.parent_span_id is not None:
|
||||||
|
parent_span_context = trace_api.SpanContext(
|
||||||
|
trace_id=span_data.trace_id,
|
||||||
|
span_id=span_data.parent_span_id,
|
||||||
|
is_remote=False,
|
||||||
|
trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED),
|
||||||
|
trace_state=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
span = ReadableSpan(
|
||||||
|
name=span_data.name,
|
||||||
|
context=span_context,
|
||||||
|
parent=parent_span_context,
|
||||||
|
resource=self.resource,
|
||||||
|
attributes=span_data.attributes,
|
||||||
|
events=span_data.events,
|
||||||
|
links=span_data.links,
|
||||||
|
kind=trace_api.SpanKind.INTERNAL,
|
||||||
|
status=span_data.status,
|
||||||
|
start_time=span_data.start_time,
|
||||||
|
end_time=span_data.end_time,
|
||||||
|
instrumentation_scope=self.instrumentation_scope,
|
||||||
|
)
|
||||||
|
return span
|
||||||
|
|
||||||
|
|
||||||
|
def generate_span_id() -> int:
|
||||||
|
span_id = random.getrandbits(64)
|
||||||
|
while span_id == INVALID_SPAN_ID:
|
||||||
|
span_id = random.getrandbits(64)
|
||||||
|
return span_id
|
||||||
|
|
||||||
|
|
||||||
|
def convert_to_trace_id(uuid_v4: Optional[str]) -> int:
|
||||||
|
try:
|
||||||
|
uuid_obj = uuid.UUID(uuid_v4)
|
||||||
|
return uuid_obj.int
|
||||||
|
except Exception as e:
|
||||||
|
raise ValueError(f"Invalid UUID input: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def convert_to_span_id(uuid_v4: Optional[str], span_type: str) -> int:
|
||||||
|
try:
|
||||||
|
uuid_obj = uuid.UUID(uuid_v4)
|
||||||
|
except Exception as e:
|
||||||
|
raise ValueError(f"Invalid UUID input: {e}")
|
||||||
|
combined_key = f"{uuid_obj.hex}-{span_type}"
|
||||||
|
hash_bytes = hashlib.sha256(combined_key.encode("utf-8")).digest()
|
||||||
|
span_id = int.from_bytes(hash_bytes[:8], byteorder="big", signed=False)
|
||||||
|
return span_id
|
||||||
|
|
||||||
|
|
||||||
|
def convert_datetime_to_nanoseconds(start_time_a: Optional[datetime]) -> Optional[int]:
|
||||||
|
if start_time_a is None:
|
||||||
|
return None
|
||||||
|
timestamp_in_seconds = start_time_a.timestamp()
|
||||||
|
timestamp_in_nanoseconds = int(timestamp_in_seconds * 1e9)
|
||||||
|
return timestamp_in_nanoseconds
|
0
api/core/ops/aliyun_trace/entities/__init__.py
Normal file
0
api/core/ops/aliyun_trace/entities/__init__.py
Normal file
21
api/core/ops/aliyun_trace/entities/aliyun_trace_entity.py
Normal file
21
api/core/ops/aliyun_trace/entities/aliyun_trace_entity.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
from collections.abc import Sequence
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from opentelemetry import trace as trace_api
|
||||||
|
from opentelemetry.sdk.trace import Event, Status, StatusCode
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
|
||||||
|
class SpanData(BaseModel):
|
||||||
|
model_config = {"arbitrary_types_allowed": True}
|
||||||
|
|
||||||
|
trace_id: int = Field(..., description="The unique identifier for the trace.")
|
||||||
|
parent_span_id: Optional[int] = Field(None, description="The ID of the parent span, if any.")
|
||||||
|
span_id: int = Field(..., description="The unique identifier for this span.")
|
||||||
|
name: str = Field(..., description="The name of the span.")
|
||||||
|
attributes: dict[str, str] = Field(default_factory=dict, description="Attributes associated with the span.")
|
||||||
|
events: Sequence[Event] = Field(default_factory=list, description="Events recorded in the span.")
|
||||||
|
links: Sequence[trace_api.Link] = Field(default_factory=list, description="Links to other spans.")
|
||||||
|
status: Status = Field(default=Status(StatusCode.UNSET), description="The status of the span.")
|
||||||
|
start_time: Optional[int] = Field(..., description="The start time of the span in nanoseconds.")
|
||||||
|
end_time: Optional[int] = Field(..., description="The end time of the span in nanoseconds.")
|
64
api/core/ops/aliyun_trace/entities/semconv.py
Normal file
64
api/core/ops/aliyun_trace/entities/semconv.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
# public
|
||||||
|
GEN_AI_SESSION_ID = "gen_ai.session.id"
|
||||||
|
|
||||||
|
GEN_AI_USER_ID = "gen_ai.user.id"
|
||||||
|
|
||||||
|
GEN_AI_USER_NAME = "gen_ai.user.name"
|
||||||
|
|
||||||
|
GEN_AI_SPAN_KIND = "gen_ai.span.kind"
|
||||||
|
|
||||||
|
GEN_AI_FRAMEWORK = "gen_ai.framework"
|
||||||
|
|
||||||
|
|
||||||
|
# Chain
|
||||||
|
INPUT_VALUE = "input.value"
|
||||||
|
|
||||||
|
OUTPUT_VALUE = "output.value"
|
||||||
|
|
||||||
|
|
||||||
|
# Retriever
|
||||||
|
RETRIEVAL_QUERY = "retrieval.query"
|
||||||
|
|
||||||
|
RETRIEVAL_DOCUMENT = "retrieval.document"
|
||||||
|
|
||||||
|
|
||||||
|
# LLM
|
||||||
|
GEN_AI_MODEL_NAME = "gen_ai.model_name"
|
||||||
|
|
||||||
|
GEN_AI_SYSTEM = "gen_ai.system"
|
||||||
|
|
||||||
|
GEN_AI_USAGE_INPUT_TOKENS = "gen_ai.usage.input_tokens"
|
||||||
|
|
||||||
|
GEN_AI_USAGE_OUTPUT_TOKENS = "gen_ai.usage.output_tokens"
|
||||||
|
|
||||||
|
GEN_AI_USAGE_TOTAL_TOKENS = "gen_ai.usage.total_tokens"
|
||||||
|
|
||||||
|
GEN_AI_PROMPT_TEMPLATE_TEMPLATE = "gen_ai.prompt_template.template"
|
||||||
|
|
||||||
|
GEN_AI_PROMPT_TEMPLATE_VARIABLE = "gen_ai.prompt_template.variable"
|
||||||
|
|
||||||
|
GEN_AI_PROMPT = "gen_ai.prompt"
|
||||||
|
|
||||||
|
GEN_AI_COMPLETION = "gem_ai.completion"
|
||||||
|
|
||||||
|
GEN_AI_RESPONSE_FINISH_REASON = "gen_ai.response.finish_reason"
|
||||||
|
|
||||||
|
# Tool
|
||||||
|
TOOL_NAME = "tool.name"
|
||||||
|
|
||||||
|
TOOL_DESCRIPTION = "tool.description"
|
||||||
|
|
||||||
|
TOOL_PARAMETERS = "tool.parameters"
|
||||||
|
|
||||||
|
|
||||||
|
class GenAISpanKind(Enum):
|
||||||
|
CHAIN = "CHAIN"
|
||||||
|
RETRIEVER = "RETRIEVER"
|
||||||
|
RERANKER = "RERANKER"
|
||||||
|
LLM = "LLM"
|
||||||
|
EMBEDDING = "EMBEDDING"
|
||||||
|
TOOL = "TOOL"
|
||||||
|
AGENT = "AGENT"
|
||||||
|
TASK = "TASK"
|
@@ -10,6 +10,7 @@ class TracingProviderEnum(StrEnum):
|
|||||||
LANGSMITH = "langsmith"
|
LANGSMITH = "langsmith"
|
||||||
OPIK = "opik"
|
OPIK = "opik"
|
||||||
WEAVE = "weave"
|
WEAVE = "weave"
|
||||||
|
ALIYUN = "aliyun"
|
||||||
|
|
||||||
|
|
||||||
class BaseTracingConfig(BaseModel):
|
class BaseTracingConfig(BaseModel):
|
||||||
@@ -184,5 +185,15 @@ class WeaveConfig(BaseTracingConfig):
|
|||||||
return v
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
class AliyunConfig(BaseTracingConfig):
|
||||||
|
"""
|
||||||
|
Model class for Aliyun tracing config.
|
||||||
|
"""
|
||||||
|
|
||||||
|
app_name: str = "dify_app"
|
||||||
|
license_key: str
|
||||||
|
endpoint: str
|
||||||
|
|
||||||
|
|
||||||
OPS_FILE_PATH = "ops_trace/"
|
OPS_FILE_PATH = "ops_trace/"
|
||||||
OPS_TRACE_FAILED_KEY = "FAILED_OPS_TRACE"
|
OPS_TRACE_FAILED_KEY = "FAILED_OPS_TRACE"
|
||||||
|
@@ -104,6 +104,17 @@ class OpsTraceProviderConfigMap(dict[str, dict[str, Any]]):
|
|||||||
"other_keys": ["project", "endpoint"],
|
"other_keys": ["project", "endpoint"],
|
||||||
"trace_instance": ArizePhoenixDataTrace,
|
"trace_instance": ArizePhoenixDataTrace,
|
||||||
}
|
}
|
||||||
|
case TracingProviderEnum.ALIYUN:
|
||||||
|
from core.ops.aliyun_trace.aliyun_trace import AliyunDataTrace
|
||||||
|
from core.ops.entities.config_entity import AliyunConfig
|
||||||
|
|
||||||
|
return {
|
||||||
|
"config_class": AliyunConfig,
|
||||||
|
"secret_keys": ["license_key"],
|
||||||
|
"other_keys": ["endpoint", "app_name"],
|
||||||
|
"trace_instance": AliyunDataTrace,
|
||||||
|
}
|
||||||
|
|
||||||
case _:
|
case _:
|
||||||
raise KeyError(f"Unsupported tracing provider: {provider}")
|
raise KeyError(f"Unsupported tracing provider: {provider}")
|
||||||
|
|
||||||
|
@@ -94,6 +94,16 @@ class OpsService:
|
|||||||
new_decrypt_tracing_config.update({"project_url": project_url})
|
new_decrypt_tracing_config.update({"project_url": project_url})
|
||||||
except Exception:
|
except Exception:
|
||||||
new_decrypt_tracing_config.update({"project_url": "https://wandb.ai/"})
|
new_decrypt_tracing_config.update({"project_url": "https://wandb.ai/"})
|
||||||
|
|
||||||
|
if tracing_provider == "aliyun" and (
|
||||||
|
"project_url" not in decrypt_tracing_config or not decrypt_tracing_config.get("project_url")
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
project_url = OpsTraceManager.get_trace_config_project_url(decrypt_tracing_config, tracing_provider)
|
||||||
|
new_decrypt_tracing_config.update({"project_url": project_url})
|
||||||
|
except Exception:
|
||||||
|
new_decrypt_tracing_config.update({"project_url": "https://arms.console.aliyun.com/"})
|
||||||
|
|
||||||
trace_config_data.tracing_config = new_decrypt_tracing_config
|
trace_config_data.tracing_config = new_decrypt_tracing_config
|
||||||
return trace_config_data.to_dict()
|
return trace_config_data.to_dict()
|
||||||
|
|
||||||
|
@@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import { useBoolean } from 'ahooks'
|
import { useBoolean } from 'ahooks'
|
||||||
import TracingIcon from './tracing-icon'
|
import TracingIcon from './tracing-icon'
|
||||||
import ProviderPanel from './provider-panel'
|
import ProviderPanel from './provider-panel'
|
||||||
import type { ArizeConfig, LangFuseConfig, LangSmithConfig, OpikConfig, PhoenixConfig, WeaveConfig } from './type'
|
import type { AliyunConfig, ArizeConfig, LangFuseConfig, LangSmithConfig, OpikConfig, PhoenixConfig, WeaveConfig } from './type'
|
||||||
import { TracingProvider } from './type'
|
import { TracingProvider } from './type'
|
||||||
import ProviderConfigModal from './provider-config-modal'
|
import ProviderConfigModal from './provider-config-modal'
|
||||||
import Indicator from '@/app/components/header/indicator'
|
import Indicator from '@/app/components/header/indicator'
|
||||||
@@ -29,7 +29,8 @@ export type PopupProps = {
|
|||||||
langFuseConfig: LangFuseConfig | null
|
langFuseConfig: LangFuseConfig | null
|
||||||
opikConfig: OpikConfig | null
|
opikConfig: OpikConfig | null
|
||||||
weaveConfig: WeaveConfig | null
|
weaveConfig: WeaveConfig | null
|
||||||
onConfigUpdated: (provider: TracingProvider, payload: ArizeConfig | PhoenixConfig | LangSmithConfig | LangFuseConfig | OpikConfig | WeaveConfig) => void
|
aliyunConfig: AliyunConfig | null
|
||||||
|
onConfigUpdated: (provider: TracingProvider, payload: ArizeConfig | PhoenixConfig | LangSmithConfig | LangFuseConfig | OpikConfig | WeaveConfig | AliyunConfig) => void
|
||||||
onConfigRemoved: (provider: TracingProvider) => void
|
onConfigRemoved: (provider: TracingProvider) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,6 +47,7 @@ const ConfigPopup: FC<PopupProps> = ({
|
|||||||
langFuseConfig,
|
langFuseConfig,
|
||||||
opikConfig,
|
opikConfig,
|
||||||
weaveConfig,
|
weaveConfig,
|
||||||
|
aliyunConfig,
|
||||||
onConfigUpdated,
|
onConfigUpdated,
|
||||||
onConfigRemoved,
|
onConfigRemoved,
|
||||||
}) => {
|
}) => {
|
||||||
@@ -69,7 +71,7 @@ const ConfigPopup: FC<PopupProps> = ({
|
|||||||
}
|
}
|
||||||
}, [onChooseProvider])
|
}, [onChooseProvider])
|
||||||
|
|
||||||
const handleConfigUpdated = useCallback((payload: ArizeConfig | PhoenixConfig | LangSmithConfig | LangFuseConfig | OpikConfig | WeaveConfig) => {
|
const handleConfigUpdated = useCallback((payload: ArizeConfig | PhoenixConfig | LangSmithConfig | LangFuseConfig | OpikConfig | WeaveConfig | AliyunConfig) => {
|
||||||
onConfigUpdated(currentProvider!, payload)
|
onConfigUpdated(currentProvider!, payload)
|
||||||
hideConfigModal()
|
hideConfigModal()
|
||||||
}, [currentProvider, hideConfigModal, onConfigUpdated])
|
}, [currentProvider, hideConfigModal, onConfigUpdated])
|
||||||
@@ -79,8 +81,8 @@ const ConfigPopup: FC<PopupProps> = ({
|
|||||||
hideConfigModal()
|
hideConfigModal()
|
||||||
}, [currentProvider, hideConfigModal, onConfigRemoved])
|
}, [currentProvider, hideConfigModal, onConfigRemoved])
|
||||||
|
|
||||||
const providerAllConfigured = arizeConfig && phoenixConfig && langSmithConfig && langFuseConfig && opikConfig && weaveConfig
|
const providerAllConfigured = arizeConfig && phoenixConfig && langSmithConfig && langFuseConfig && opikConfig && weaveConfig && aliyunConfig
|
||||||
const providerAllNotConfigured = !arizeConfig && !phoenixConfig && !langSmithConfig && !langFuseConfig && !opikConfig && !weaveConfig
|
const providerAllNotConfigured = !arizeConfig && !phoenixConfig && !langSmithConfig && !langFuseConfig && !opikConfig && !weaveConfig && !aliyunConfig
|
||||||
|
|
||||||
const switchContent = (
|
const switchContent = (
|
||||||
<Switch
|
<Switch
|
||||||
@@ -167,6 +169,19 @@ const ConfigPopup: FC<PopupProps> = ({
|
|||||||
key="weave-provider-panel"
|
key="weave-provider-panel"
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const aliyunPanel = (
|
||||||
|
<ProviderPanel
|
||||||
|
type={TracingProvider.aliyun}
|
||||||
|
readOnly={readOnly}
|
||||||
|
config={aliyunConfig}
|
||||||
|
hasConfigured={!!aliyunConfig}
|
||||||
|
onConfig={handleOnConfig(TracingProvider.aliyun)}
|
||||||
|
isChosen={chosenProvider === TracingProvider.aliyun}
|
||||||
|
onChoose={handleOnChoose(TracingProvider.aliyun)}
|
||||||
|
key="alyun-provider-panel"
|
||||||
|
/>
|
||||||
|
)
|
||||||
const configuredProviderPanel = () => {
|
const configuredProviderPanel = () => {
|
||||||
const configuredPanels: JSX.Element[] = []
|
const configuredPanels: JSX.Element[] = []
|
||||||
|
|
||||||
@@ -188,6 +203,9 @@ const ConfigPopup: FC<PopupProps> = ({
|
|||||||
if (phoenixConfig)
|
if (phoenixConfig)
|
||||||
configuredPanels.push(phoenixPanel)
|
configuredPanels.push(phoenixPanel)
|
||||||
|
|
||||||
|
if (aliyunConfig)
|
||||||
|
configuredPanels.push(aliyunPanel)
|
||||||
|
|
||||||
return configuredPanels
|
return configuredPanels
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,6 +230,9 @@ const ConfigPopup: FC<PopupProps> = ({
|
|||||||
if (!weaveConfig)
|
if (!weaveConfig)
|
||||||
notConfiguredPanels.push(weavePanel)
|
notConfiguredPanels.push(weavePanel)
|
||||||
|
|
||||||
|
if (!aliyunConfig)
|
||||||
|
notConfiguredPanels.push(aliyunPanel)
|
||||||
|
|
||||||
return notConfiguredPanels
|
return notConfiguredPanels
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,6 +247,8 @@ const ConfigPopup: FC<PopupProps> = ({
|
|||||||
return langFuseConfig
|
return langFuseConfig
|
||||||
if (currentProvider === TracingProvider.opik)
|
if (currentProvider === TracingProvider.opik)
|
||||||
return opikConfig
|
return opikConfig
|
||||||
|
if (currentProvider === TracingProvider.aliyun)
|
||||||
|
return aliyunConfig
|
||||||
return weaveConfig
|
return weaveConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,6 +296,7 @@ const ConfigPopup: FC<PopupProps> = ({
|
|||||||
{weavePanel}
|
{weavePanel}
|
||||||
{arizePanel}
|
{arizePanel}
|
||||||
{phoenixPanel}
|
{phoenixPanel}
|
||||||
|
{aliyunPanel}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
@@ -7,4 +7,5 @@ export const docURL = {
|
|||||||
[TracingProvider.langfuse]: 'https://docs.langfuse.com',
|
[TracingProvider.langfuse]: 'https://docs.langfuse.com',
|
||||||
[TracingProvider.opik]: 'https://www.comet.com/docs/opik/tracing/integrations/dify#setup-instructions',
|
[TracingProvider.opik]: 'https://www.comet.com/docs/opik/tracing/integrations/dify#setup-instructions',
|
||||||
[TracingProvider.weave]: 'https://weave-docs.wandb.ai/',
|
[TracingProvider.weave]: 'https://weave-docs.wandb.ai/',
|
||||||
|
[TracingProvider.aliyun]: 'https://help.aliyun.com/zh/arms/tracing-analysis/untitled-document-1750672984680',
|
||||||
}
|
}
|
||||||
|
@@ -7,12 +7,12 @@ import {
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { usePathname } from 'next/navigation'
|
import { usePathname } from 'next/navigation'
|
||||||
import { useBoolean } from 'ahooks'
|
import { useBoolean } from 'ahooks'
|
||||||
import type { ArizeConfig, LangFuseConfig, LangSmithConfig, OpikConfig, PhoenixConfig, WeaveConfig } from './type'
|
import type { AliyunConfig, ArizeConfig, LangFuseConfig, LangSmithConfig, OpikConfig, PhoenixConfig, WeaveConfig } from './type'
|
||||||
import { TracingProvider } from './type'
|
import { TracingProvider } from './type'
|
||||||
import TracingIcon from './tracing-icon'
|
import TracingIcon from './tracing-icon'
|
||||||
import ConfigButton from './config-button'
|
import ConfigButton from './config-button'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import { ArizeIcon, LangfuseIcon, LangsmithIcon, OpikIcon, PhoenixIcon, WeaveIcon } from '@/app/components/base/icons/src/public/tracing'
|
import { AliyunIcon, ArizeIcon, LangfuseIcon, LangsmithIcon, OpikIcon, PhoenixIcon, WeaveIcon } from '@/app/components/base/icons/src/public/tracing'
|
||||||
import Indicator from '@/app/components/header/indicator'
|
import Indicator from '@/app/components/header/indicator'
|
||||||
import { fetchTracingConfig as doFetchTracingConfig, fetchTracingStatus, updateTracingStatus } from '@/service/apps'
|
import { fetchTracingConfig as doFetchTracingConfig, fetchTracingStatus, updateTracingStatus } from '@/service/apps'
|
||||||
import type { TracingStatus } from '@/models/app'
|
import type { TracingStatus } from '@/models/app'
|
||||||
@@ -69,6 +69,7 @@ const Panel: FC = () => {
|
|||||||
[TracingProvider.langfuse]: LangfuseIcon,
|
[TracingProvider.langfuse]: LangfuseIcon,
|
||||||
[TracingProvider.opik]: OpikIcon,
|
[TracingProvider.opik]: OpikIcon,
|
||||||
[TracingProvider.weave]: WeaveIcon,
|
[TracingProvider.weave]: WeaveIcon,
|
||||||
|
[TracingProvider.aliyun]: AliyunIcon,
|
||||||
}
|
}
|
||||||
const InUseProviderIcon = inUseTracingProvider ? providerIconMap[inUseTracingProvider] : undefined
|
const InUseProviderIcon = inUseTracingProvider ? providerIconMap[inUseTracingProvider] : undefined
|
||||||
|
|
||||||
@@ -78,7 +79,8 @@ const Panel: FC = () => {
|
|||||||
const [langFuseConfig, setLangFuseConfig] = useState<LangFuseConfig | null>(null)
|
const [langFuseConfig, setLangFuseConfig] = useState<LangFuseConfig | null>(null)
|
||||||
const [opikConfig, setOpikConfig] = useState<OpikConfig | null>(null)
|
const [opikConfig, setOpikConfig] = useState<OpikConfig | null>(null)
|
||||||
const [weaveConfig, setWeaveConfig] = useState<WeaveConfig | null>(null)
|
const [weaveConfig, setWeaveConfig] = useState<WeaveConfig | null>(null)
|
||||||
const hasConfiguredTracing = !!(langSmithConfig || langFuseConfig || opikConfig || weaveConfig || arizeConfig || phoenixConfig)
|
const [aliyunConfig, setAliyunConfig] = useState<AliyunConfig | null>(null)
|
||||||
|
const hasConfiguredTracing = !!(langSmithConfig || langFuseConfig || opikConfig || weaveConfig || arizeConfig || phoenixConfig || aliyunConfig)
|
||||||
|
|
||||||
const fetchTracingConfig = async () => {
|
const fetchTracingConfig = async () => {
|
||||||
const { tracing_config: arizeConfig, has_not_configured: arizeHasNotConfig } = await doFetchTracingConfig({ appId, provider: TracingProvider.arize })
|
const { tracing_config: arizeConfig, has_not_configured: arizeHasNotConfig } = await doFetchTracingConfig({ appId, provider: TracingProvider.arize })
|
||||||
@@ -99,6 +101,9 @@ const Panel: FC = () => {
|
|||||||
const { tracing_config: weaveConfig, has_not_configured: weaveHasNotConfig } = await doFetchTracingConfig({ appId, provider: TracingProvider.weave })
|
const { tracing_config: weaveConfig, has_not_configured: weaveHasNotConfig } = await doFetchTracingConfig({ appId, provider: TracingProvider.weave })
|
||||||
if (!weaveHasNotConfig)
|
if (!weaveHasNotConfig)
|
||||||
setWeaveConfig(weaveConfig as WeaveConfig)
|
setWeaveConfig(weaveConfig as WeaveConfig)
|
||||||
|
const { tracing_config: aliyunConfig, has_not_configured: aliyunHasNotConfig } = await doFetchTracingConfig({ appId, provider: TracingProvider.aliyun })
|
||||||
|
if (!aliyunHasNotConfig)
|
||||||
|
setAliyunConfig(aliyunConfig as AliyunConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleTracingConfigUpdated = async (provider: TracingProvider) => {
|
const handleTracingConfigUpdated = async (provider: TracingProvider) => {
|
||||||
@@ -116,6 +121,8 @@ const Panel: FC = () => {
|
|||||||
setOpikConfig(tracing_config as OpikConfig)
|
setOpikConfig(tracing_config as OpikConfig)
|
||||||
else if (provider === TracingProvider.weave)
|
else if (provider === TracingProvider.weave)
|
||||||
setWeaveConfig(tracing_config as WeaveConfig)
|
setWeaveConfig(tracing_config as WeaveConfig)
|
||||||
|
else if (provider === TracingProvider.aliyun)
|
||||||
|
setAliyunConfig(tracing_config as AliyunConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleTracingConfigRemoved = (provider: TracingProvider) => {
|
const handleTracingConfigRemoved = (provider: TracingProvider) => {
|
||||||
@@ -131,6 +138,8 @@ const Panel: FC = () => {
|
|||||||
setOpikConfig(null)
|
setOpikConfig(null)
|
||||||
else if (provider === TracingProvider.weave)
|
else if (provider === TracingProvider.weave)
|
||||||
setWeaveConfig(null)
|
setWeaveConfig(null)
|
||||||
|
else if (provider === TracingProvider.aliyun)
|
||||||
|
setAliyunConfig(null)
|
||||||
if (provider === inUseTracingProvider) {
|
if (provider === inUseTracingProvider) {
|
||||||
handleTracingStatusChange({
|
handleTracingStatusChange({
|
||||||
enabled: false,
|
enabled: false,
|
||||||
@@ -191,6 +200,7 @@ const Panel: FC = () => {
|
|||||||
langFuseConfig={langFuseConfig}
|
langFuseConfig={langFuseConfig}
|
||||||
opikConfig={opikConfig}
|
opikConfig={opikConfig}
|
||||||
weaveConfig={weaveConfig}
|
weaveConfig={weaveConfig}
|
||||||
|
aliyunConfig={aliyunConfig}
|
||||||
onConfigUpdated={handleTracingConfigUpdated}
|
onConfigUpdated={handleTracingConfigUpdated}
|
||||||
onConfigRemoved={handleTracingConfigRemoved}
|
onConfigRemoved={handleTracingConfigRemoved}
|
||||||
controlShowPopup={controlShowPopup}
|
controlShowPopup={controlShowPopup}
|
||||||
@@ -228,6 +238,7 @@ const Panel: FC = () => {
|
|||||||
langFuseConfig={langFuseConfig}
|
langFuseConfig={langFuseConfig}
|
||||||
opikConfig={opikConfig}
|
opikConfig={opikConfig}
|
||||||
weaveConfig={weaveConfig}
|
weaveConfig={weaveConfig}
|
||||||
|
aliyunConfig={aliyunConfig}
|
||||||
onConfigUpdated={handleTracingConfigUpdated}
|
onConfigUpdated={handleTracingConfigUpdated}
|
||||||
onConfigRemoved={handleTracingConfigRemoved}
|
onConfigRemoved={handleTracingConfigRemoved}
|
||||||
controlShowPopup={controlShowPopup}
|
controlShowPopup={controlShowPopup}
|
||||||
|
@@ -4,7 +4,7 @@ import React, { useCallback, useState } from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useBoolean } from 'ahooks'
|
import { useBoolean } from 'ahooks'
|
||||||
import Field from './field'
|
import Field from './field'
|
||||||
import type { ArizeConfig, LangFuseConfig, LangSmithConfig, OpikConfig, PhoenixConfig, WeaveConfig } from './type'
|
import type { AliyunConfig, ArizeConfig, LangFuseConfig, LangSmithConfig, OpikConfig, PhoenixConfig, WeaveConfig } from './type'
|
||||||
import { TracingProvider } from './type'
|
import { TracingProvider } from './type'
|
||||||
import { docURL } from './config'
|
import { docURL } from './config'
|
||||||
import {
|
import {
|
||||||
@@ -22,10 +22,10 @@ import Divider from '@/app/components/base/divider'
|
|||||||
type Props = {
|
type Props = {
|
||||||
appId: string
|
appId: string
|
||||||
type: TracingProvider
|
type: TracingProvider
|
||||||
payload?: ArizeConfig | PhoenixConfig | LangSmithConfig | LangFuseConfig | OpikConfig | WeaveConfig | null
|
payload?: ArizeConfig | PhoenixConfig | LangSmithConfig | LangFuseConfig | OpikConfig | WeaveConfig | AliyunConfig | null
|
||||||
onRemoved: () => void
|
onRemoved: () => void
|
||||||
onCancel: () => void
|
onCancel: () => void
|
||||||
onSaved: (payload: ArizeConfig | PhoenixConfig | LangSmithConfig | LangFuseConfig | OpikConfig | WeaveConfig) => void
|
onSaved: (payload: ArizeConfig | PhoenixConfig | LangSmithConfig | LangFuseConfig | OpikConfig | WeaveConfig | AliyunConfig) => void
|
||||||
onChosen: (provider: TracingProvider) => void
|
onChosen: (provider: TracingProvider) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,6 +71,12 @@ const weaveConfigTemplate = {
|
|||||||
host: '',
|
host: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const aliyunConfigTemplate = {
|
||||||
|
app_name: '',
|
||||||
|
license_key: '',
|
||||||
|
endpoint: '',
|
||||||
|
}
|
||||||
|
|
||||||
const ProviderConfigModal: FC<Props> = ({
|
const ProviderConfigModal: FC<Props> = ({
|
||||||
appId,
|
appId,
|
||||||
type,
|
type,
|
||||||
@@ -84,7 +90,7 @@ const ProviderConfigModal: FC<Props> = ({
|
|||||||
const isEdit = !!payload
|
const isEdit = !!payload
|
||||||
const isAdd = !isEdit
|
const isAdd = !isEdit
|
||||||
const [isSaving, setIsSaving] = useState(false)
|
const [isSaving, setIsSaving] = useState(false)
|
||||||
const [config, setConfig] = useState<ArizeConfig | PhoenixConfig | LangSmithConfig | LangFuseConfig | OpikConfig | WeaveConfig>((() => {
|
const [config, setConfig] = useState<ArizeConfig | PhoenixConfig | LangSmithConfig | LangFuseConfig | OpikConfig | WeaveConfig | AliyunConfig>((() => {
|
||||||
if (isEdit)
|
if (isEdit)
|
||||||
return payload
|
return payload
|
||||||
|
|
||||||
@@ -103,6 +109,9 @@ const ProviderConfigModal: FC<Props> = ({
|
|||||||
else if (type === TracingProvider.opik)
|
else if (type === TracingProvider.opik)
|
||||||
return opikConfigTemplate
|
return opikConfigTemplate
|
||||||
|
|
||||||
|
else if (type === TracingProvider.aliyun)
|
||||||
|
return aliyunConfigTemplate
|
||||||
|
|
||||||
return weaveConfigTemplate
|
return weaveConfigTemplate
|
||||||
})())
|
})())
|
||||||
const [isShowRemoveConfirm, {
|
const [isShowRemoveConfirm, {
|
||||||
@@ -183,6 +192,16 @@ const ProviderConfigModal: FC<Props> = ({
|
|||||||
errorMessage = t('common.errorMsg.fieldRequired', { field: t(`${I18N_PREFIX}.project`) })
|
errorMessage = t('common.errorMsg.fieldRequired', { field: t(`${I18N_PREFIX}.project`) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === TracingProvider.aliyun) {
|
||||||
|
const postData = config as AliyunConfig
|
||||||
|
if (!errorMessage && !postData.app_name)
|
||||||
|
errorMessage = t('common.errorMsg.fieldRequired', { field: 'App Name' })
|
||||||
|
if (!errorMessage && !postData.license_key)
|
||||||
|
errorMessage = t('common.errorMsg.fieldRequired', { field: 'License Key' })
|
||||||
|
if (!errorMessage && !postData.endpoint)
|
||||||
|
errorMessage = t('common.errorMsg.fieldRequired', { field: 'Endpoint' })
|
||||||
|
}
|
||||||
|
|
||||||
return errorMessage
|
return errorMessage
|
||||||
}, [config, t, type])
|
}, [config, t, type])
|
||||||
const handleSave = useCallback(async () => {
|
const handleSave = useCallback(async () => {
|
||||||
@@ -294,6 +313,31 @@ const ProviderConfigModal: FC<Props> = ({
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{type === TracingProvider.aliyun && (
|
||||||
|
<>
|
||||||
|
<Field
|
||||||
|
label='License Key'
|
||||||
|
labelClassName='!text-sm'
|
||||||
|
isRequired
|
||||||
|
value={(config as AliyunConfig).license_key}
|
||||||
|
onChange={handleConfigChange('license_key')}
|
||||||
|
placeholder={t(`${I18N_PREFIX}.placeholder`, { key: 'License Key' })!}
|
||||||
|
/>
|
||||||
|
<Field
|
||||||
|
label='Endpoint'
|
||||||
|
labelClassName='!text-sm'
|
||||||
|
value={(config as AliyunConfig).endpoint}
|
||||||
|
onChange={handleConfigChange('endpoint')}
|
||||||
|
placeholder={'https://tracing.arms.aliyuncs.com'}
|
||||||
|
/>
|
||||||
|
<Field
|
||||||
|
label='App Name'
|
||||||
|
labelClassName='!text-sm'
|
||||||
|
value={(config as AliyunConfig).app_name}
|
||||||
|
onChange={handleConfigChange('app_name')}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
{type === TracingProvider.weave && (
|
{type === TracingProvider.weave && (
|
||||||
<>
|
<>
|
||||||
<Field
|
<Field
|
||||||
|
@@ -7,7 +7,7 @@ import {
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { TracingProvider } from './type'
|
import { TracingProvider } from './type'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import { ArizeIconBig, LangfuseIconBig, LangsmithIconBig, OpikIconBig, PhoenixIconBig, WeaveIconBig } from '@/app/components/base/icons/src/public/tracing'
|
import { AliyunIconBig, ArizeIconBig, LangfuseIconBig, LangsmithIconBig, OpikIconBig, PhoenixIconBig, WeaveIconBig } from '@/app/components/base/icons/src/public/tracing'
|
||||||
import { Eye as View } from '@/app/components/base/icons/src/vender/solid/general'
|
import { Eye as View } from '@/app/components/base/icons/src/vender/solid/general'
|
||||||
|
|
||||||
const I18N_PREFIX = 'app.tracing'
|
const I18N_PREFIX = 'app.tracing'
|
||||||
@@ -30,6 +30,7 @@ const getIcon = (type: TracingProvider) => {
|
|||||||
[TracingProvider.langfuse]: LangfuseIconBig,
|
[TracingProvider.langfuse]: LangfuseIconBig,
|
||||||
[TracingProvider.opik]: OpikIconBig,
|
[TracingProvider.opik]: OpikIconBig,
|
||||||
[TracingProvider.weave]: WeaveIconBig,
|
[TracingProvider.weave]: WeaveIconBig,
|
||||||
|
[TracingProvider.aliyun]: AliyunIconBig,
|
||||||
})[type]
|
})[type]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -5,6 +5,7 @@ export enum TracingProvider {
|
|||||||
langfuse = 'langfuse',
|
langfuse = 'langfuse',
|
||||||
opik = 'opik',
|
opik = 'opik',
|
||||||
weave = 'weave',
|
weave = 'weave',
|
||||||
|
aliyun = 'aliyun',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ArizeConfig = {
|
export type ArizeConfig = {
|
||||||
@@ -46,3 +47,9 @@ export type WeaveConfig = {
|
|||||||
endpoint: string
|
endpoint: string
|
||||||
host: string
|
host: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type AliyunConfig = {
|
||||||
|
app_name: string
|
||||||
|
license_key: string
|
||||||
|
endpoint: string
|
||||||
|
}
|
||||||
|
@@ -77,7 +77,7 @@ const ExtraInfo = ({ isMobile, relatedApps, expand }: IExtraInfoProps) => {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isMobile && <div className={classNames('uppercase text-xs text-text-tertiary font-medium pb-2 pt-4', 'flex items-center justify-center !px-0 gap-1')}>
|
{isMobile && <div className={classNames('pb-2 pt-4 text-xs font-medium uppercase text-text-tertiary', 'flex items-center justify-center gap-1 !px-0')}>
|
||||||
{relatedAppsTotal || '--'}
|
{relatedAppsTotal || '--'}
|
||||||
<PaperClipIcon className='h-4 w-4 text-text-secondary' />
|
<PaperClipIcon className='h-4 w-4 text-text-secondary' />
|
||||||
</div>}
|
</div>}
|
||||||
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 14 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 10 KiB |
118
web/app/components/base/icons/src/public/tracing/AliyunIcon.json
Normal file
118
web/app/components/base/icons/src/public/tracing/AliyunIcon.json
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
{
|
||||||
|
"icon": {
|
||||||
|
"type": "element",
|
||||||
|
"isRootNode": true,
|
||||||
|
"name": "svg",
|
||||||
|
"attributes": {
|
||||||
|
"xmlns": "http://www.w3.org/2000/svg",
|
||||||
|
"xmlns:xlink": "http://www.w3.org/1999/xlink",
|
||||||
|
"fill": "none",
|
||||||
|
"version": "1.1",
|
||||||
|
"width": "65",
|
||||||
|
"height": "16",
|
||||||
|
"viewBox": "0 0 65 16"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "defs",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "clipPath",
|
||||||
|
"attributes": {
|
||||||
|
"id": "master_svg0_42_34281"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "rect",
|
||||||
|
"attributes": {
|
||||||
|
"x": "0",
|
||||||
|
"y": "0",
|
||||||
|
"width": "19",
|
||||||
|
"height": "16",
|
||||||
|
"rx": "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "g",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "g",
|
||||||
|
"attributes": {
|
||||||
|
"clip-path": "url(#master_svg0_42_34281)"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "g",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "g",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "path",
|
||||||
|
"attributes": {
|
||||||
|
"d": "M4.06862,14.6667C3.79213,14.6667,3.45463,14.5688,3.05614,14.373C2.97908,14.3351,2.92692,14.3105,2.89968,14.2992C2.33193,14.0628,1.82911,13.7294,1.39123,13.2989C0.463742,12.3871,0,11.2874,0,10C0,8.71258,0.463742,7.61293,1.39123,6.70107C2.16172,5.94358,3.06404,5.50073,4.09819,5.37252C4.23172,3.98276,4.81755,2.77756,5.85569,1.75693C7.04708,0.585642,8.4857,0,10.1716,0C11.5256,0,12.743,0.396982,13.8239,1.19095C14.8847,1.97019,15.61,2.97855,16,4.21604L14.7045,4.61063C14.4016,3.64918,13.8374,2.86532,13.0121,2.25905C12.1719,1.64191,11.2251,1.33333,10.1716,1.33333C8.8602,1.33333,7.74124,1.7888,6.81467,2.69974C5.88811,3.61067,5.42483,4.71076,5.42483,6L5.42483,6.66667L4.74673,6.66667C3.81172,6.66667,3.01288,6.99242,2.35021,7.64393C1.68754,8.2954,1.35621,9.08076,1.35621,10C1.35621,10.9192,1.68754,11.7046,2.35021,12.3561C2.66354,12.6641,3.02298,12.9026,3.42852,13.0714C3.48193,13.0937,3.55988,13.13,3.66237,13.1803C3.87004,13.2823,4.00545,13.3333,4.06862,13.3333L4.06862,14.6667Z",
|
||||||
|
"fill-rule": "evenodd",
|
||||||
|
"fill": "#FF6A00",
|
||||||
|
"fill-opacity": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "g",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "path",
|
||||||
|
"attributes": {
|
||||||
|
"d": "M13.458613505859375,7.779393492279053C12.975613505859375,7.717463492279053,12.484813505859375,7.686503492279053,11.993983505859376,7.686503492279053C11.152583505859376,7.686503492279053,10.303403505859375,7.779393492279053,9.493183505859374,7.941943492279053C8.682953505859375,8.104503492279052,7.903893505859375,8.359943492279053,7.155983505859375,8.654083492279053C6.657383505859375,8.870823492279053,6.158783505859375,9.128843492279053,5.660181505859375,9.428153492279053C5.332974751859375,9.621673492279053,5.239486705859375,10.070633492279054,5.434253505859375,10.395743492279053L7.413073505859375,13.298533492279052C7.639003505859375,13.623603492279052,8.090863505859375,13.716463492279052,8.418073505859375,13.523003492279052C8.547913505859375,13.435263492279052,8.763453505859374,13.326893492279053,9.064693505859374,13.197863492279053C9.516553505859374,13.004333492279052,9.976203505859374,12.872733492279053,10.459223505859375,12.779863492279052C10.942243505859375,12.679263492279052,11.433053505859375,12.617333492279052,11.955023505859375,12.617333492279052L13.380683505859375,7.810353492279052L13.458613505859375,7.779393492279053ZM15.273813505859374,8.135463492279053L15.016753505859375,5.333333492279053L13.458613505859375,7.787133492279053C13.817013505859375,7.818093492279052,14.144213505859375,7.880023492279053,14.494743505859375,7.949683492279053C14.494743505859375,7.944523492279053,14.754433505859375,8.006453492279054,15.273813505859374,8.135463492279053ZM12.064083505859376,12.648273492279053L11.378523505859375,14.970463492279054L12.515943505859376,16.00003349227905L14.074083505859376,15.643933492279054L14.525943505859376,13.027603492279052C14.198743505859374,12.934663492279054,13.879283505859375,12.834063492279054,13.552083505859375,12.772133492279053C13.069083505859375,12.717933492279052,12.578283505859375,12.648273492279053,12.064083505859376,12.648273492279053ZM18.327743505859374,9.428153492279053C17.829143505859374,9.128843492279053,17.330543505859374,8.870823492279053,16.831943505859375,8.654083492279053C16.348943505859374,8.460573492279053,15.826943505859376,8.267053492279054,15.305013505859375,8.135463492279053L15.305013505859375,8.267053492279054L14.463613505859374,13.043063492279053C14.596083505859376,13.105003492279053,14.759683505859375,13.135933492279053,14.884283505859376,13.205603492279053C15.185523505859376,13.334623492279052,15.401043505859375,13.443003492279052,15.530943505859375,13.530733492279053C15.858143505859376,13.724263492279054,16.341143505859375,13.623603492279052,16.535943505859375,13.306263492279053L18.514743505859375,10.403483492279053C18.779643505859376,10.039673492279054,18.686143505859377,9.621673492279053,18.327743505859374,9.428153492279053Z",
|
||||||
|
"fill": "#FF6A00",
|
||||||
|
"fill-opacity": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "g",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "g",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "path",
|
||||||
|
"attributes": {
|
||||||
|
"d": "M25.044,2.668L34.676,2.668L34.676,4.04L25.044,4.04L25.044,2.668ZM29.958,7.82Q29.258,9.066,28.355,10.41Q27.451999999999998,11.754,26.92,12.3L32.506,11.782Q31.442,10.158,30.84,9.346L32.058,8.562000000000001Q32.786,9.5,33.843,11.012Q34.9,12.524,35.516,13.546L34.214,14.526Q33.891999999999996,13.966,33.346000000000004,13.098Q32.016,13.182,29.734,13.378Q27.451999999999998,13.574,25.87,13.742L25.31,13.812L24.834,13.882L24.414,12.468Q24.708,12.37,24.862000000000002,12.265Q25.016,12.16,25.121,12.069Q25.226,11.978,25.268,11.936Q25.912,11.32,26.724,10.165Q27.536,9.01,28.208,7.82L23.854,7.82L23.854,6.434L35.866,6.434L35.866,7.82L29.958,7.82ZM42.656,7.414L42.656,8.576L41.354,8.576L41.354,1.814L42.656,1.87L42.656,7.036Q43.314,5.846,43.888000000000005,4.369Q44.462,2.892,44.714,1.6600000000000001L46.086,1.981999Q45.96,2.612,45.722,3.41L49.6,3.41L49.6,4.74L45.274,4.74Q44.616,6.56,43.706,8.128L42.656,7.414ZM38.596000000000004,2.346L39.884,2.402L39.884,8.212L38.596000000000004,8.212L38.596000000000004,2.346ZM46.184,4.964Q46.688,5.356,47.5,6.175Q48.312,6.994,48.788,7.582L47.751999999999995,8.59Q47.346000000000004,8.072,46.576,7.274Q45.806,6.476,45.204,5.902L46.184,4.964ZM48.41,9.01L48.41,12.706L49.894,12.706L49.894,13.966L37.391999999999996,13.966L37.391999999999996,12.706L38.848,12.706L38.848,9.01L48.41,9.01ZM41.676,10.256L40.164,10.256L40.164,12.706L41.676,12.706L41.676,10.256ZM42.908,12.706L44.364000000000004,12.706L44.364000000000004,10.256L42.908,10.256L42.908,12.706ZM45.582,12.706L47.108000000000004,12.706L47.108000000000004,10.256L45.582,10.256L45.582,12.706ZM54.906,7.456L55.116,8.394L54.178,8.814L54.178,12.818Q54.178,13.434,54.031,13.735Q53.884,14.036,53.534,14.162Q53.184,14.288,52.456,14.358L51.867999999999995,14.414L51.476,13.084L52.162,13.028Q52.512,13,52.652,12.958Q52.792,12.916,52.841,12.797Q52.89,12.678,52.89,12.384L52.89,9.36Q51.980000000000004,9.724,51.322,9.948L51.013999999999996,8.576Q51.798,8.324,52.89,7.876L52.89,5.524L51.42,5.524L51.42,4.166L52.89,4.166L52.89,1.7579989999999999L54.178,1.814L54.178,4.166L55.214,4.166L55.214,5.524L54.178,5.524L54.178,7.316L54.808,7.022L54.906,7.456ZM56.894,4.5440000000000005L56.894,6.098L55.564,6.098L55.564,3.256L58.686,3.256Q58.42,2.346,58.266,1.9260000000000002L59.624,1.7579989999999999Q59.848,2.276,60.142,3.256L63.25,3.256L63.25,6.098L61.962,6.098L61.962,4.5440000000000005L56.894,4.5440000000000005ZM59.008,6.322Q58.392,6.938,57.685,7.512Q56.978,8.086,55.956,8.841999999999999L55.242,7.764Q56.824,6.728,58.126,5.37L59.008,6.322ZM60.422,5.37Q61.024,5.776,62.095,6.581Q63.166,7.386,63.656,7.806L62.942,8.982Q62.368,8.45,61.332,7.652Q60.296,6.854,59.666,6.434L60.422,5.37ZM62.592,10.256L60.044,10.256L60.044,12.566L63.572,12.566L63.572,13.826L55.144,13.826L55.144,12.566L58.63,12.566L58.63,10.256L56.054,10.256L56.054,8.982L62.592,8.982L62.592,10.256Z",
|
||||||
|
"fill": "#FF6A00",
|
||||||
|
"fill-opacity": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name": "AliyunIcon"
|
||||||
|
}
|
@@ -0,0 +1,16 @@
|
|||||||
|
// GENERATE BY script
|
||||||
|
// DON NOT EDIT IT MANUALLY
|
||||||
|
|
||||||
|
import * as React from 'react'
|
||||||
|
import data from './AliyunIcon.json'
|
||||||
|
import IconBase from '@/app/components/base/icons/IconBase'
|
||||||
|
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||||
|
|
||||||
|
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||||
|
props,
|
||||||
|
ref,
|
||||||
|
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||||
|
|
||||||
|
Icon.displayName = 'AliyunIcon'
|
||||||
|
|
||||||
|
export default Icon
|
@@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
"icon": {
|
||||||
|
"type": "element",
|
||||||
|
"isRootNode": true,
|
||||||
|
"name": "svg",
|
||||||
|
"attributes": {
|
||||||
|
"xmlns": "http://www.w3.org/2000/svg",
|
||||||
|
"xmlns:xlink": "http://www.w3.org/1999/xlink",
|
||||||
|
"fill": "none",
|
||||||
|
"version": "1.1",
|
||||||
|
"width": "96",
|
||||||
|
"height": "24",
|
||||||
|
"viewBox": "0 0 96 24"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "g",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "g",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "path",
|
||||||
|
"attributes": {
|
||||||
|
"d": "M6.10294,22C5.68819,22,5.18195,21.8532,4.58421,21.5595C4.46861,21.5027,4.39038,21.4658,4.34951,21.4488C3.49789,21.0943,2.74367,20.5941,2.08684,19.9484C0.695613,18.5806,0,16.9311,0,15C0,13.0689,0.695612,11.4194,2.08684,10.0516C3.24259,8.91537,4.59607,8.2511,6.14728,8.05878C6.34758,5.97414,7.22633,4.16634,8.78354,2.63539C10.5706,0.878463,12.7286,0,15.2573,0C17.2884,0,19.1146,0.595472,20.7358,1.78642C22.327,2.95528,23.4151,4.46783,24,6.32406L22.0568,6.91594C21.6024,5.47377,20.7561,4.29798,19.5181,3.38858C18.2579,2.46286,16.8377,2,15.2573,2C13.2903,2,11.6119,2.6832,10.222,4.04961C8.83217,5.41601,8.13725,7.06614,8.13725,9L8.13725,10L7.12009,10C5.71758,10,4.51932,10.4886,3.52532,11.4659C2.53132,12.4431,2.03431,13.6211,2.03431,15C2.03431,16.3789,2.53132,17.5569,3.52532,18.5341C3.99531,18.9962,4.53447,19.3538,5.14278,19.6071C5.2229,19.6405,5.33983,19.695,5.49356,19.7705C5.80505,19.9235,6.00818,20,6.10294,20L6.10294,22Z",
|
||||||
|
"fill-rule": "evenodd",
|
||||||
|
"fill": "#FF6A00",
|
||||||
|
"fill-opacity": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "g",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "path",
|
||||||
|
"attributes": {
|
||||||
|
"d": "M20.18796103515625,11.66909C19.46346103515625,11.5762,18.72726103515625,11.52975,17.991011035156248,11.52975C16.728921035156247,11.52975,15.45515103515625,11.66909,14.23981103515625,11.91292C13.02447103515625,12.156749999999999,11.85588103515625,12.539909999999999,10.73402103515625,12.98113C9.98612103515625,13.306239999999999,9.23822103515625,13.69327,8.49031803515625,14.14223C7.99950790415625,14.43251,7.85927603515625,15.10595,8.15142503515625,15.59361L11.11966103515625,19.9478C11.45855103515625,20.4354,12.13634103515625,20.5747,12.627151035156249,20.2845C12.821921035156251,20.152900000000002,13.14523103515625,19.990299999999998,13.59708103515625,19.796799999999998C14.27487103515625,19.506500000000003,14.964341035156249,19.3091,15.68887103515625,19.169800000000002C16.413401035156248,19.018900000000002,17.14962103515625,18.926000000000002,17.93258103515625,18.926000000000002L20.071061035156248,11.715530000000001L20.18796103515625,11.66909ZM22.91076103515625,12.20319L22.525161035156252,8L20.18796103515625,11.6807C20.72556103515625,11.72714,21.21636103515625,11.82003,21.74216103515625,11.92453C21.74216103515625,11.91679,22.13166103515625,12.00968,22.91076103515625,12.20319ZM18.09616103515625,18.9724L17.06782103515625,22.4557L18.773961035156248,24L21.11116103515625,23.465899999999998L21.788961035156248,19.5414C21.298161035156248,19.402,20.81896103515625,19.2511,20.32816103515625,19.1582C19.60366103515625,19.076900000000002,18.86746103515625,18.9724,18.09616103515625,18.9724ZM27.49166103515625,14.14223C26.74376103515625,13.69327,25.99586103515625,13.306239999999999,25.24796103515625,12.98113C24.52346103515625,12.69086,23.74046103515625,12.40058,22.95756103515625,12.20319L22.95756103515625,12.40058L21.69546103515625,19.5646C21.89416103515625,19.6575,22.139561035156248,19.7039,22.32646103515625,19.8084C22.77836103515625,20.0019,23.101661035156248,20.1645,23.29646103515625,20.2961C23.78726103515625,20.586399999999998,24.51176103515625,20.4354,24.80396103515625,19.959400000000002L27.77216103515625,15.605229999999999C28.16946103515625,15.05951,28.02926103515625,14.43251,27.49166103515625,14.14223Z",
|
||||||
|
"fill": "#FF6A00",
|
||||||
|
"fill-opacity": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "g",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "path",
|
||||||
|
"attributes": {
|
||||||
|
"d": "M35.785,3.8624638671875L50.233000000000004,3.8624638671875L50.233000000000004,5.9204638671875L35.785,5.9204638671875L35.785,3.8624638671875ZM43.156,11.5904638671875Q42.106,13.4594638671875,40.7515,15.4754638671875Q39.397,17.4914638671875,38.599000000000004,18.3104638671875L46.978,17.5334638671875Q45.382,15.0974638671875,44.479,13.8794638671875L46.306,12.7034638671875Q47.397999999999996,14.1104638671875,48.9835,16.3784638671875Q50.569,18.6464638671875,51.492999999999995,20.1794638671875L49.54,21.6494638671875Q49.057,20.8094638671875,48.238,19.5074638671875Q46.243,19.6334638671875,42.82,19.9274638671875Q39.397,20.2214638671875,37.024,20.4734638671875L36.184,20.5784638671875L35.47,20.6834638671875L34.84,18.5624638671875Q35.281,18.4154638671875,35.512,18.2579638671875Q35.743,18.1004638671875,35.9005,17.963963867187502Q36.058,17.8274638671875,36.121,17.7644638671875Q37.087,16.840463867187502,38.305,15.1079638671875Q39.522999999999996,13.3754638671875,40.531,11.5904638671875L34,11.5904638671875L34,9.5114638671875L52.018,9.5114638671875L52.018,11.5904638671875L43.156,11.5904638671875ZM62.203,10.9814638671875L62.203,12.7244638671875L60.25,12.7244638671875L60.25,2.5814638671875L62.203,2.6654638671875L62.203,10.4144638671875Q63.19,8.6294638671875,64.051,6.4139638671875Q64.912,4.1984638671875,65.28999999999999,2.3504638671875L67.348,2.8334628671875Q67.15899999999999,3.7784638671875,66.80199999999999,4.9754638671875L72.619,4.9754638671875L72.619,6.9704638671875L66.13,6.9704638671875Q65.143,9.7004638671875,63.778,12.0524638671875L62.203,10.9814638671875ZM56.113,3.3794638671875L58.045,3.4634638671875L58.045,12.1784638671875L56.113,12.1784638671875L56.113,3.3794638671875ZM67.495,7.3064638671875Q68.251,7.8944638671875,69.469,9.1229638671875Q70.687,10.3514638671875,71.40100000000001,11.2334638671875L69.84700000000001,12.7454638671875Q69.238,11.9684638671875,68.083,10.7714638671875Q66.928,9.5744638671875,66.025,8.7134638671875L67.495,7.3064638671875ZM70.834,13.3754638671875L70.834,18.9194638671875L73.06,18.9194638671875L73.06,20.8094638671875L54.307,20.8094638671875L54.307,18.9194638671875L56.491,18.9194638671875L56.491,13.3754638671875L70.834,13.3754638671875ZM60.733000000000004,15.2444638671875L58.465,15.2444638671875L58.465,18.9194638671875L60.733000000000004,18.9194638671875L60.733000000000004,15.2444638671875ZM62.581,18.9194638671875L64.765,18.9194638671875L64.765,15.2444638671875L62.581,15.2444638671875L62.581,18.9194638671875ZM66.592,18.9194638671875L68.881,18.9194638671875L68.881,15.2444638671875L66.592,15.2444638671875L66.592,18.9194638671875ZM80.578,11.0444638671875L80.893,12.4514638671875L79.48599999999999,13.0814638671875L79.48599999999999,19.0874638671875Q79.48599999999999,20.0114638671875,79.2655,20.4629638671875Q79.045,20.9144638671875,78.52000000000001,21.1034638671875Q77.995,21.2924638671875,76.90299999999999,21.3974638671875L76.021,21.4814638671875L75.43299999999999,19.4864638671875L76.462,19.4024638671875Q76.987,19.3604638671875,77.197,19.2974638671875Q77.407,19.2344638671875,77.4805,19.0559638671875Q77.554,18.8774638671875,77.554,18.4364638671875L77.554,13.9004638671875Q76.189,14.4464638671875,75.202,14.7824638671875L74.74000000000001,12.7244638671875Q75.916,12.3464638671875,77.554,11.6744638671875L77.554,8.1464638671875L75.34899999999999,8.1464638671875L75.34899999999999,6.1094638671875L77.554,6.1094638671875L77.554,2.4974628671875L79.48599999999999,2.5814638671875L79.48599999999999,6.1094638671875L81.03999999999999,6.1094638671875L81.03999999999999,8.1464638671875L79.48599999999999,8.1464638671875L79.48599999999999,10.8344638671875L80.431,10.3934638671875L80.578,11.0444638671875ZM83.56,6.6764638671875L83.56,9.0074638671875L81.565,9.0074638671875L81.565,4.7444638671875L86.24799999999999,4.7444638671875Q85.84899999999999,3.3794638671875,85.618,2.7494638671875L87.655,2.4974628671875Q87.991,3.2744638671875,88.432,4.7444638671875L93.094,4.7444638671875L93.094,9.0074638671875L91.162,9.0074638671875L91.162,6.6764638671875L83.56,6.6764638671875ZM86.731,9.3434638671875Q85.807,10.2674638671875,84.7465,11.1284638671875Q83.686,11.9894638671875,82.15299999999999,13.1234638671875L81.082,11.5064638671875Q83.455,9.9524638671875,85.408,7.9154638671875L86.731,9.3434638671875ZM88.852,7.9154638671875Q89.755,8.5244638671875,91.3615,9.731963867187499Q92.968,10.9394638671875,93.703,11.5694638671875L92.632,13.3334638671875Q91.771,12.5354638671875,90.217,11.3384638671875Q88.663,10.1414638671875,87.718,9.5114638671875L88.852,7.9154638671875ZM92.107,15.2444638671875L88.285,15.2444638671875L88.285,18.7094638671875L93.577,18.7094638671875L93.577,20.5994638671875L80.935,20.5994638671875L80.935,18.7094638671875L86.164,18.7094638671875L86.164,15.2444638671875L82.3,15.2444638671875L82.3,13.3334638671875L92.107,13.3334638671875L92.107,15.2444638671875Z",
|
||||||
|
"fill": "#FF6A00",
|
||||||
|
"fill-opacity": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name": "AliyunBigIcon"
|
||||||
|
}
|
@@ -0,0 +1,16 @@
|
|||||||
|
// GENERATE BY script
|
||||||
|
// DON NOT EDIT IT MANUALLY
|
||||||
|
|
||||||
|
import * as React from 'react'
|
||||||
|
import data from './AliyunIconBig.json'
|
||||||
|
import IconBase from '@/app/components/base/icons/IconBase'
|
||||||
|
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||||
|
|
||||||
|
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||||
|
props,
|
||||||
|
ref,
|
||||||
|
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||||
|
|
||||||
|
Icon.displayName = 'AliyunIconBig'
|
||||||
|
|
||||||
|
export default Icon
|
@@ -11,3 +11,5 @@ export { default as PhoenixIcon } from './PhoenixIcon'
|
|||||||
export { default as TracingIcon } from './TracingIcon'
|
export { default as TracingIcon } from './TracingIcon'
|
||||||
export { default as WeaveIconBig } from './WeaveIconBig'
|
export { default as WeaveIconBig } from './WeaveIconBig'
|
||||||
export { default as WeaveIcon } from './WeaveIcon'
|
export { default as WeaveIcon } from './WeaveIcon'
|
||||||
|
export { default as AliyunIconBig } from './AliyunIconBig'
|
||||||
|
export { default as AliyunIcon } from './AliyunIcon'
|
||||||
|
@@ -116,7 +116,7 @@ const Select: FC<ISelectProps> = ({
|
|||||||
if (!disabled)
|
if (!disabled)
|
||||||
setOpen(!open)
|
setOpen(!open)
|
||||||
}
|
}
|
||||||
} className={classNames(`flex items-center h-9 w-full rounded-lg border-0 ${bgClassName} py-1.5 pl-3 pr-10 shadow-sm sm:text-sm sm:leading-6 focus-visible:outline-none focus-visible:bg-state-base-hover group-hover:bg-state-base-hover`, optionClassName)}>
|
} className={classNames(`flex h-9 w-full items-center rounded-lg border-0 ${bgClassName} py-1.5 pl-3 pr-10 shadow-sm focus-visible:bg-state-base-hover focus-visible:outline-none group-hover:bg-state-base-hover sm:text-sm sm:leading-6`, optionClassName)}>
|
||||||
<div className='w-0 grow truncate text-left' title={selectedItem?.name}>{selectedItem?.name}</div>
|
<div className='w-0 grow truncate text-left' title={selectedItem?.name}>{selectedItem?.name}</div>
|
||||||
</ComboboxButton>}
|
</ComboboxButton>}
|
||||||
<ComboboxButton className="absolute inset-y-0 right-0 flex items-center rounded-r-md px-2 focus:outline-none" onClick={
|
<ComboboxButton className="absolute inset-y-0 right-0 flex items-center rounded-r-md px-2 focus:outline-none" onClick={
|
||||||
@@ -137,7 +137,7 @@ const Select: FC<ISelectProps> = ({
|
|||||||
value={item}
|
value={item}
|
||||||
className={({ active }: { active: boolean }) =>
|
className={({ active }: { active: boolean }) =>
|
||||||
classNames(
|
classNames(
|
||||||
'relative cursor-default select-none py-2 pl-3 pr-9 rounded-lg hover:bg-state-base-hover text-text-secondary',
|
'relative cursor-default select-none rounded-lg py-2 pl-3 pr-9 text-text-secondary hover:bg-state-base-hover',
|
||||||
active ? 'bg-state-base-hover' : '',
|
active ? 'bg-state-base-hover' : '',
|
||||||
optionClassName,
|
optionClassName,
|
||||||
)
|
)
|
||||||
@@ -225,8 +225,8 @@ const SimpleSelect: FC<ISelectProps> = ({
|
|||||||
if (listboxRef.current)
|
if (listboxRef.current)
|
||||||
onOpenChange?.(listboxRef.current.getAttribute('data-open') !== null)
|
onOpenChange?.(listboxRef.current.getAttribute('data-open') !== null)
|
||||||
})
|
})
|
||||||
}} className={classNames(`flex items-center w-full h-full rounded-lg border-0 bg-components-input-bg-normal pl-3 pr-10 sm:text-sm sm:leading-6 focus-visible:outline-none focus-visible:bg-state-base-hover-alt group-hover/simple-select:bg-state-base-hover-alt ${disabled ? 'cursor-not-allowed' : 'cursor-pointer'}`, className)}>
|
}} className={classNames(`flex h-full w-full items-center rounded-lg border-0 bg-components-input-bg-normal pl-3 pr-10 focus-visible:bg-state-base-hover-alt focus-visible:outline-none group-hover/simple-select:bg-state-base-hover-alt sm:text-sm sm:leading-6 ${disabled ? 'cursor-not-allowed' : 'cursor-pointer'}`, className)}>
|
||||||
<span className={classNames('block truncate text-left system-sm-regular text-components-input-text-filled', !selectedItem?.name && 'text-components-input-text-placeholder')}>{selectedItem?.name ?? localPlaceholder}</span>
|
<span className={classNames('system-sm-regular block truncate text-left text-components-input-text-filled', !selectedItem?.name && 'text-components-input-text-placeholder')}>{selectedItem?.name ?? localPlaceholder}</span>
|
||||||
<span className="absolute inset-y-0 right-0 flex items-center pr-2">
|
<span className="absolute inset-y-0 right-0 flex items-center pr-2">
|
||||||
{isLoading ? <RiLoader4Line className='h-3.5 w-3.5 animate-spin text-text-secondary' />
|
{isLoading ? <RiLoader4Line className='h-3.5 w-3.5 animate-spin text-text-secondary' />
|
||||||
: (selectedItem && !notClearable)
|
: (selectedItem && !notClearable)
|
||||||
@@ -252,13 +252,13 @@ const SimpleSelect: FC<ISelectProps> = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{(!disabled) && (
|
{(!disabled) && (
|
||||||
<ListboxOptions className={classNames('absolute z-10 mt-1 px-1 max-h-60 w-full overflow-auto rounded-xl bg-components-panel-bg-blur backdrop-blur-sm py-1 text-base shadow-lg border-components-panel-border border-[0.5px] focus:outline-none sm:text-sm', optionWrapClassName)}>
|
<ListboxOptions className={classNames('absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur px-1 py-1 text-base shadow-lg backdrop-blur-sm focus:outline-none sm:text-sm', optionWrapClassName)}>
|
||||||
{items.map((item: Item) => (
|
{items.map((item: Item) => (
|
||||||
<ListboxOption
|
<ListboxOption
|
||||||
key={item.value}
|
key={item.value}
|
||||||
className={
|
className={
|
||||||
classNames(
|
classNames(
|
||||||
'relative cursor-pointer select-none py-2 pl-3 pr-9 rounded-lg hover:bg-state-base-hover text-text-secondary',
|
'relative cursor-pointer select-none rounded-lg py-2 pl-3 pr-9 text-text-secondary hover:bg-state-base-hover',
|
||||||
optionClassName,
|
optionClassName,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -338,7 +338,7 @@ const PortalSelect: FC<PortalSelectProps> = ({
|
|||||||
: (
|
: (
|
||||||
<div
|
<div
|
||||||
className={classNames(`
|
className={classNames(`
|
||||||
group flex items-center justify-between px-2.5 h-9 rounded-lg border-0 bg-components-input-bg-normal hover:bg-state-base-hover-alt text-sm ${readonly ? 'cursor-not-allowed' : 'cursor-pointer'}
|
group flex h-9 items-center justify-between rounded-lg border-0 bg-components-input-bg-normal px-2.5 text-sm hover:bg-state-base-hover-alt ${readonly ? 'cursor-not-allowed' : 'cursor-pointer'}
|
||||||
`, triggerClassName, triggerClassNameFn?.(open))}
|
`, triggerClassName, triggerClassNameFn?.(open))}
|
||||||
title={selectedItem?.name}
|
title={selectedItem?.name}
|
||||||
>
|
>
|
||||||
@@ -358,7 +358,7 @@ const PortalSelect: FC<PortalSelectProps> = ({
|
|||||||
</PortalToFollowElemTrigger>
|
</PortalToFollowElemTrigger>
|
||||||
<PortalToFollowElemContent className={`z-20 ${popupClassName}`}>
|
<PortalToFollowElemContent className={`z-20 ${popupClassName}`}>
|
||||||
<div
|
<div
|
||||||
className={classNames('px-1 py-1 max-h-60 overflow-auto rounded-md text-base shadow-lg border-components-panel-border bg-components-panel-bg border-[0.5px] focus:outline-none sm:text-sm', popupInnerClassName)}
|
className={classNames('max-h-60 overflow-auto rounded-md border-[0.5px] border-components-panel-border bg-components-panel-bg px-1 py-1 text-base shadow-lg focus:outline-none sm:text-sm', popupInnerClassName)}
|
||||||
>
|
>
|
||||||
{items.map((item: Item) => (
|
{items.map((item: Item) => (
|
||||||
<div
|
<div
|
||||||
|
@@ -54,7 +54,7 @@ const Nav = ({
|
|||||||
<div
|
<div
|
||||||
onClick={() => setAppDetail()}
|
onClick={() => setAppDetail()}
|
||||||
className={classNames(`
|
className={classNames(`
|
||||||
flex items-center h-7 px-2.5 cursor-pointer rounded-[10px]
|
flex h-7 cursor-pointer items-center rounded-[10px] px-2.5
|
||||||
${isActivated ? 'text-components-main-nav-nav-button-text-active' : 'text-components-main-nav-nav-button-text'}
|
${isActivated ? 'text-components-main-nav-nav-button-text-active' : 'text-components-main-nav-nav-button-text'}
|
||||||
${curNav && isActivated && 'hover:bg-components-main-nav-nav-button-bg-active-hover'}
|
${curNav && isActivated && 'hover:bg-components-main-nav-nav-button-bg-active-hover'}
|
||||||
`)}
|
`)}
|
||||||
|
@@ -31,8 +31,8 @@ const PluginsNav = ({
|
|||||||
)}>
|
)}>
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'relative flex flex-row h-8 p-1.5 gap-0.5 border border-transparent items-center justify-center rounded-xl system-sm-medium',
|
'system-sm-medium relative flex h-8 flex-row items-center justify-center gap-0.5 rounded-xl border border-transparent p-1.5',
|
||||||
activated && 'border-components-main-nav-nav-button-border bg-components-main-nav-nav-button-bg-active shadow-md text-components-main-nav-nav-button-text',
|
activated && 'border-components-main-nav-nav-button-border bg-components-main-nav-nav-button-bg-active text-components-main-nav-nav-button-text shadow-md',
|
||||||
!activated && 'text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
|
!activated && 'text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
|
||||||
(isInstallingWithError || isFailed) && !activated && 'border-components-panel-border-subtle',
|
(isInstallingWithError || isFailed) && !activated && 'border-components-panel-border-subtle',
|
||||||
)}
|
)}
|
||||||
|
@@ -22,7 +22,7 @@ const ToolsNav = ({
|
|||||||
return (
|
return (
|
||||||
<Link href="/tools" className={classNames(
|
<Link href="/tools" className={classNames(
|
||||||
'group text-sm font-medium',
|
'group text-sm font-medium',
|
||||||
activated && 'font-semibold bg-components-main-nav-nav-button-bg-active hover:bg-components-main-nav-nav-button-bg-active-hover shadow-md',
|
activated && 'hover:bg-components-main-nav-nav-button-bg-active-hover bg-components-main-nav-nav-button-bg-active font-semibold shadow-md',
|
||||||
activated ? 'text-components-main-nav-nav-button-text-active' : 'text-components-main-nav-nav-button-text hover:bg-components-main-nav-nav-button-bg-hover',
|
activated ? 'text-components-main-nav-nav-button-text-active' : 'text-components-main-nav-nav-button-text hover:bg-components-main-nav-nav-button-bg-hover',
|
||||||
className,
|
className,
|
||||||
)}>
|
)}>
|
||||||
|
@@ -98,7 +98,7 @@ const CodeEditor: FC<CodeEditorProps> = ({
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames('flex flex-col h-full bg-components-input-bg-normal overflow-hidden', hideTopMenu && 'pt-2', className)}>
|
<div className={classNames('flex h-full flex-col overflow-hidden bg-components-input-bg-normal', hideTopMenu && 'pt-2', className)}>
|
||||||
{!hideTopMenu && (
|
{!hideTopMenu && (
|
||||||
<div className='flex items-center justify-between pl-2 pr-1 pt-1'>
|
<div className='flex items-center justify-between pl-2 pr-1 pt-1'>
|
||||||
<div className='system-xs-semibold-uppercase py-0.5 text-text-secondary'>
|
<div className='system-xs-semibold-uppercase py-0.5 text-text-secondary'>
|
||||||
|
@@ -32,7 +32,7 @@ export default function SocialAuth(props: SocialAuthProps) {
|
|||||||
<span className={
|
<span className={
|
||||||
classNames(
|
classNames(
|
||||||
style.githubIcon,
|
style.githubIcon,
|
||||||
'w-5 h-5 mr-2',
|
'mr-2 h-5 w-5',
|
||||||
)
|
)
|
||||||
} />
|
} />
|
||||||
<span className="truncate">{t('login.withGitHub')}</span>
|
<span className="truncate">{t('login.withGitHub')}</span>
|
||||||
@@ -50,7 +50,7 @@ export default function SocialAuth(props: SocialAuthProps) {
|
|||||||
<span className={
|
<span className={
|
||||||
classNames(
|
classNames(
|
||||||
style.googleIcon,
|
style.googleIcon,
|
||||||
'w-5 h-5 mr-2',
|
'mr-2 h-5 w-5',
|
||||||
)
|
)
|
||||||
} />
|
} />
|
||||||
<span className="truncate">{t('login.withGoogle')}</span>
|
<span className="truncate">{t('login.withGoogle')}</span>
|
||||||
|
@@ -174,6 +174,10 @@ const translation = {
|
|||||||
title: 'Weave',
|
title: 'Weave',
|
||||||
description: 'Weave is an open-source platform for evaluating, testing, and monitoring LLM applications.',
|
description: 'Weave is an open-source platform for evaluating, testing, and monitoring LLM applications.',
|
||||||
},
|
},
|
||||||
|
aliyun: {
|
||||||
|
title: 'LLM observability',
|
||||||
|
description: 'The SaaS observability platform provided by Alibaba Cloud enables out of box monitoring, tracing, and evaluation of Dify applications.',
|
||||||
|
},
|
||||||
inUse: 'In use',
|
inUse: 'In use',
|
||||||
configProvider: {
|
configProvider: {
|
||||||
title: 'Config ',
|
title: 'Config ',
|
||||||
|
@@ -185,6 +185,10 @@ const translation = {
|
|||||||
title: '编织',
|
title: '编织',
|
||||||
description: 'Weave 是一个开源平台,用于评估、测试和监控大型语言模型应用程序。',
|
description: 'Weave 是一个开源平台,用于评估、测试和监控大型语言模型应用程序。',
|
||||||
},
|
},
|
||||||
|
aliyun: {
|
||||||
|
title: '大模型可观测',
|
||||||
|
description: '阿里云提供的SaaS化可观测平台,一键开启Dify应用的监控追踪和评估。',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
appSelector: {
|
appSelector: {
|
||||||
label: '应用',
|
label: '应用',
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
import type { ArizeConfig, LangFuseConfig, LangSmithConfig, OpikConfig, PhoenixConfig, TracingProvider, WeaveConfig } from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/type'
|
import type { AliyunConfig, LangFuseConfig, LangSmithConfig, OpikConfig, PhoenixConfig, TracingProvider, WeaveConfig } from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/type'
|
||||||
import type { App, AppTemplate, SiteConfig } from '@/types/app'
|
import type { App, AppTemplate, SiteConfig } from '@/types/app'
|
||||||
import type { Dependency } from '@/app/components/plugins/types'
|
import type { Dependency } from '@/app/components/plugins/types'
|
||||||
|
|
||||||
/* export type App = {
|
/* export type App = {
|
||||||
id: string
|
id: strin
|
||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
mode: AppMode
|
mode: AppMode
|
||||||
@@ -166,5 +166,5 @@ export type TracingStatus = {
|
|||||||
|
|
||||||
export type TracingConfig = {
|
export type TracingConfig = {
|
||||||
tracing_provider: TracingProvider
|
tracing_provider: TracingProvider
|
||||||
tracing_config: ArizeConfig | PhoenixConfig | LangSmithConfig | LangFuseConfig | OpikConfig | WeaveConfig
|
tracing_config: ArizeConfig | PhoenixConfig | LangSmithConfig | LangFuseConfig | OpikConfig | WeaveConfig | AliyunConfig
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user