diff --git a/api/controllers/service_api/app/completion.py b/api/controllers/service_api/app/completion.py index 1d9890199d8521..7762672494dd6a 100644 --- a/api/controllers/service_api/app/completion.py +++ b/api/controllers/service_api/app/completion.py @@ -1,5 +1,6 @@ import logging +from flask import request from flask_restful import Resource, reqparse from werkzeug.exceptions import InternalServerError, NotFound @@ -23,6 +24,7 @@ ProviderTokenNotInitError, QuotaExceededError, ) +from core.helper.trace_id_helper import get_external_trace_id from core.model_runtime.errors.invoke import InvokeError from libs import helper from libs.helper import uuid_value @@ -111,6 +113,10 @@ def post(self, app_model: App, end_user: EndUser): args = parser.parse_args() + external_trace_id = get_external_trace_id(request) + if external_trace_id: + args["external_trace_id"] = external_trace_id + streaming = args["response_mode"] == "streaming" try: diff --git a/api/controllers/service_api/app/workflow.py b/api/controllers/service_api/app/workflow.py index ac2ebf2b0979aa..370ff911b4c9da 100644 --- a/api/controllers/service_api/app/workflow.py +++ b/api/controllers/service_api/app/workflow.py @@ -1,6 +1,7 @@ import logging from dateutil.parser import isoparse +from flask import request from flask_restful import Resource, fields, marshal_with, reqparse from flask_restful.inputs import int_range from sqlalchemy.orm import Session, sessionmaker @@ -23,6 +24,7 @@ ProviderTokenNotInitError, QuotaExceededError, ) +from core.helper.trace_id_helper import get_external_trace_id from core.model_runtime.errors.invoke import InvokeError from core.workflow.entities.workflow_execution import WorkflowExecutionStatus from extensions.ext_database import db @@ -90,7 +92,9 @@ def post(self, app_model: App, end_user: EndUser): parser.add_argument("files", type=list, required=False, location="json") parser.add_argument("response_mode", type=str, choices=["blocking", "streaming"], location="json") args = parser.parse_args() - + external_trace_id = get_external_trace_id(request) + if external_trace_id: + args["external_trace_id"] = external_trace_id streaming = args.get("response_mode") == "streaming" try: diff --git a/api/core/app/apps/advanced_chat/app_generator.py b/api/core/app/apps/advanced_chat/app_generator.py index bd5ad9c51bcde7..fc6556dfb5b73b 100644 --- a/api/core/app/apps/advanced_chat/app_generator.py +++ b/api/core/app/apps/advanced_chat/app_generator.py @@ -23,6 +23,7 @@ from core.app.apps.message_based_app_queue_manager import MessageBasedAppQueueManager from core.app.entities.app_invoke_entities import AdvancedChatAppGenerateEntity, InvokeFrom from core.app.entities.task_entities import ChatbotAppBlockingResponse, ChatbotAppStreamResponse +from core.helper.trace_id_helper import extract_external_trace_id_from_args from core.model_runtime.errors.invoke import InvokeAuthorizationError from core.ops.ops_trace_manager import TraceQueueManager from core.prompt.utils.get_thread_messages_length import get_thread_messages_length @@ -112,7 +113,10 @@ def generate( query = query.replace("\x00", "") inputs = args["inputs"] - extras = {"auto_generate_conversation_name": args.get("auto_generate_name", False)} + extras = { + "auto_generate_conversation_name": args.get("auto_generate_name", False), + **extract_external_trace_id_from_args(args), + } # get conversation conversation = None diff --git a/api/core/app/apps/advanced_chat/generate_task_pipeline.py b/api/core/app/apps/advanced_chat/generate_task_pipeline.py index 337b779b504e07..dc27076a4d5eae 100644 --- a/api/core/app/apps/advanced_chat/generate_task_pipeline.py +++ b/api/core/app/apps/advanced_chat/generate_task_pipeline.py @@ -559,6 +559,7 @@ def _handle_workflow_succeeded_event( outputs=event.outputs, conversation_id=self._conversation_id, trace_manager=trace_manager, + external_trace_id=self._application_generate_entity.extras.get("external_trace_id"), ) workflow_finish_resp = self._workflow_response_converter.workflow_finish_to_stream_response( session=session, @@ -590,6 +591,7 @@ def _handle_workflow_partial_success_event( exceptions_count=event.exceptions_count, conversation_id=None, trace_manager=trace_manager, + external_trace_id=self._application_generate_entity.extras.get("external_trace_id"), ) workflow_finish_resp = self._workflow_response_converter.workflow_finish_to_stream_response( session=session, @@ -622,6 +624,7 @@ def _handle_workflow_failed_event( conversation_id=self._conversation_id, trace_manager=trace_manager, exceptions_count=event.exceptions_count, + external_trace_id=self._application_generate_entity.extras.get("external_trace_id"), ) workflow_finish_resp = self._workflow_response_converter.workflow_finish_to_stream_response( session=session, @@ -653,6 +656,7 @@ def _handle_stop_event( error_message=event.get_stop_reason(), conversation_id=self._conversation_id, trace_manager=trace_manager, + external_trace_id=self._application_generate_entity.extras.get("external_trace_id"), ) workflow_finish_resp = self._workflow_response_converter.workflow_finish_to_stream_response( session=session, diff --git a/api/core/app/apps/workflow/app_generator.py b/api/core/app/apps/workflow/app_generator.py index 6f560b32535352..eeca9bb5030884 100644 --- a/api/core/app/apps/workflow/app_generator.py +++ b/api/core/app/apps/workflow/app_generator.py @@ -22,6 +22,7 @@ from core.app.apps.workflow.generate_task_pipeline import WorkflowAppGenerateTaskPipeline from core.app.entities.app_invoke_entities import InvokeFrom, WorkflowAppGenerateEntity from core.app.entities.task_entities import WorkflowAppBlockingResponse, WorkflowAppStreamResponse +from core.helper.trace_id_helper import extract_external_trace_id_from_args from core.model_runtime.errors.invoke import InvokeAuthorizationError from core.ops.ops_trace_manager import TraceQueueManager from core.repositories import DifyCoreRepositoryFactory @@ -123,6 +124,10 @@ def generate( ) inputs: Mapping[str, Any] = args["inputs"] + + extras = { + **extract_external_trace_id_from_args(args), + } workflow_run_id = str(uuid.uuid4()) # init application generate entity application_generate_entity = WorkflowAppGenerateEntity( @@ -142,6 +147,7 @@ def generate( call_depth=call_depth, trace_manager=trace_manager, workflow_execution_id=workflow_run_id, + extras=extras, ) contexts.plugin_tool_providers.set({}) diff --git a/api/core/app/apps/workflow/generate_task_pipeline.py b/api/core/app/apps/workflow/generate_task_pipeline.py index 9a39b2e01e60ce..e31a316c569271 100644 --- a/api/core/app/apps/workflow/generate_task_pipeline.py +++ b/api/core/app/apps/workflow/generate_task_pipeline.py @@ -490,6 +490,7 @@ def _handle_workflow_succeeded_event( outputs=event.outputs, conversation_id=None, trace_manager=trace_manager, + external_trace_id=self._application_generate_entity.extras.get("external_trace_id"), ) # save workflow app log @@ -524,6 +525,7 @@ def _handle_workflow_partial_success_event( exceptions_count=event.exceptions_count, conversation_id=None, trace_manager=trace_manager, + external_trace_id=self._application_generate_entity.extras.get("external_trace_id"), ) # save workflow app log @@ -561,6 +563,7 @@ def _handle_workflow_failed_and_stop_events( conversation_id=None, trace_manager=trace_manager, exceptions_count=event.exceptions_count if isinstance(event, QueueWorkflowFailedEvent) else 0, + external_trace_id=self._application_generate_entity.extras.get("external_trace_id"), ) # save workflow app log diff --git a/api/core/helper/trace_id_helper.py b/api/core/helper/trace_id_helper.py new file mode 100644 index 00000000000000..e90c3194f284f7 --- /dev/null +++ b/api/core/helper/trace_id_helper.py @@ -0,0 +1,42 @@ +import re +from collections.abc import Mapping +from typing import Any, Optional + + +def is_valid_trace_id(trace_id: str) -> bool: + """ + Check if the trace_id is valid. + + Requirements: 1-128 characters, only letters, numbers, '-', and '_'. + """ + return bool(re.match(r"^[a-zA-Z0-9\-_]{1,128}$", trace_id)) + + +def get_external_trace_id(request: Any) -> Optional[str]: + """ + Retrieve the trace_id from the request. + + Priority: header ('X-Trace-Id'), then parameters, then JSON body. Returns None if not provided or invalid. + """ + trace_id = request.headers.get("X-Trace-Id") + if not trace_id: + trace_id = request.args.get("trace_id") + if not trace_id and getattr(request, "is_json", False): + json_data = getattr(request, "json", None) + if json_data: + trace_id = json_data.get("trace_id") + if isinstance(trace_id, str) and is_valid_trace_id(trace_id): + return trace_id + return None + + +def extract_external_trace_id_from_args(args: Mapping[str, Any]) -> dict: + """ + Extract 'external_trace_id' from args. + + Returns a dict suitable for use in extras. Returns an empty dict if not found. + """ + trace_id = args.get("external_trace_id") + if trace_id: + return {"external_trace_id": trace_id} + return {} diff --git a/api/core/ops/aliyun_trace/aliyun_trace.py b/api/core/ops/aliyun_trace/aliyun_trace.py index db8fec4ee9c1f8..bbbc12a2c82095 100644 --- a/api/core/ops/aliyun_trace/aliyun_trace.py +++ b/api/core/ops/aliyun_trace/aliyun_trace.py @@ -101,7 +101,8 @@ def get_project_url(self): 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) + external_trace_id = trace_info.metadata.get("external_trace_id") + trace_id = external_trace_id or 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) diff --git a/api/core/ops/arize_phoenix_trace/arize_phoenix_trace.py b/api/core/ops/arize_phoenix_trace/arize_phoenix_trace.py index 8b3ce0c44805e6..14dba442378e22 100644 --- a/api/core/ops/arize_phoenix_trace/arize_phoenix_trace.py +++ b/api/core/ops/arize_phoenix_trace/arize_phoenix_trace.py @@ -153,7 +153,8 @@ def workflow_trace(self, trace_info: WorkflowTraceInfo): } workflow_metadata.update(trace_info.metadata) - trace_id = uuid_to_trace_id(trace_info.workflow_run_id) + external_trace_id = trace_info.metadata.get("external_trace_id") + trace_id = external_trace_id or uuid_to_trace_id(trace_info.workflow_run_id) span_id = RandomIdGenerator().generate_span_id() context = SpanContext( trace_id=trace_id, diff --git a/api/core/ops/langfuse_trace/langfuse_trace.py b/api/core/ops/langfuse_trace/langfuse_trace.py index 4a7e66d27c8d04..6dadb2897ec8cf 100644 --- a/api/core/ops/langfuse_trace/langfuse_trace.py +++ b/api/core/ops/langfuse_trace/langfuse_trace.py @@ -67,13 +67,14 @@ def trace(self, trace_info: BaseTraceInfo): self.generate_name_trace(trace_info) def workflow_trace(self, trace_info: WorkflowTraceInfo): - trace_id = trace_info.workflow_run_id + external_trace_id = trace_info.metadata.get("external_trace_id") + trace_id = external_trace_id or trace_info.workflow_run_id user_id = trace_info.metadata.get("user_id") metadata = trace_info.metadata metadata["workflow_app_log_id"] = trace_info.workflow_app_log_id if trace_info.message_id: - trace_id = trace_info.message_id + trace_id = external_trace_id or trace_info.message_id name = TraceTaskName.MESSAGE_TRACE.value trace_data = LangfuseTrace( id=trace_id, diff --git a/api/core/ops/langsmith_trace/langsmith_trace.py b/api/core/ops/langsmith_trace/langsmith_trace.py index 8a559c4929bae9..3246782278a22b 100644 --- a/api/core/ops/langsmith_trace/langsmith_trace.py +++ b/api/core/ops/langsmith_trace/langsmith_trace.py @@ -65,7 +65,8 @@ def trace(self, trace_info: BaseTraceInfo): self.generate_name_trace(trace_info) def workflow_trace(self, trace_info: WorkflowTraceInfo): - trace_id = trace_info.message_id or trace_info.workflow_run_id + external_trace_id = trace_info.metadata.get("external_trace_id") + trace_id = external_trace_id or trace_info.message_id or trace_info.workflow_run_id if trace_info.start_time is None: trace_info.start_time = datetime.now() message_dotted_order = ( diff --git a/api/core/ops/opik_trace/opik_trace.py b/api/core/ops/opik_trace/opik_trace.py index be4997a5bf3690..dfa7052c363ea7 100644 --- a/api/core/ops/opik_trace/opik_trace.py +++ b/api/core/ops/opik_trace/opik_trace.py @@ -96,7 +96,8 @@ def trace(self, trace_info: BaseTraceInfo): self.generate_name_trace(trace_info) def workflow_trace(self, trace_info: WorkflowTraceInfo): - dify_trace_id = trace_info.workflow_run_id + external_trace_id = trace_info.metadata.get("external_trace_id") + dify_trace_id = external_trace_id or trace_info.workflow_run_id opik_trace_id = prepare_opik_uuid(trace_info.start_time, dify_trace_id) workflow_metadata = wrap_metadata( trace_info.metadata, message_id=trace_info.message_id, workflow_app_log_id=trace_info.workflow_app_log_id @@ -104,7 +105,7 @@ def workflow_trace(self, trace_info: WorkflowTraceInfo): root_span_id = None if trace_info.message_id: - dify_trace_id = trace_info.message_id + dify_trace_id = external_trace_id or trace_info.message_id opik_trace_id = prepare_opik_uuid(trace_info.start_time, dify_trace_id) trace_data = { diff --git a/api/core/ops/ops_trace_manager.py b/api/core/ops/ops_trace_manager.py index 5c9b9d27b78b20..34963efab3572c 100644 --- a/api/core/ops/ops_trace_manager.py +++ b/api/core/ops/ops_trace_manager.py @@ -520,6 +520,10 @@ def workflow_trace( "app_id": workflow_run.app_id, } + external_trace_id = self.kwargs.get("external_trace_id") + if external_trace_id: + metadata["external_trace_id"] = external_trace_id + workflow_trace_info = WorkflowTraceInfo( workflow_data=workflow_run.to_dict(), conversation_id=conversation_id, diff --git a/api/core/ops/weave_trace/weave_trace.py b/api/core/ops/weave_trace/weave_trace.py index 445c6a87412285..4bd41ce4a686de 100644 --- a/api/core/ops/weave_trace/weave_trace.py +++ b/api/core/ops/weave_trace/weave_trace.py @@ -87,7 +87,8 @@ def trace(self, trace_info: BaseTraceInfo): self.generate_name_trace(trace_info) def workflow_trace(self, trace_info: WorkflowTraceInfo): - trace_id = trace_info.message_id or trace_info.workflow_run_id + external_trace_id = trace_info.metadata.get("external_trace_id") + trace_id = external_trace_id or trace_info.message_id or trace_info.workflow_run_id if trace_info.start_time is None: trace_info.start_time = datetime.now() diff --git a/api/core/workflow/workflow_cycle_manager.py b/api/core/workflow/workflow_cycle_manager.py index 3e591ef885c387..1206633d8e53c1 100644 --- a/api/core/workflow/workflow_cycle_manager.py +++ b/api/core/workflow/workflow_cycle_manager.py @@ -85,6 +85,7 @@ def handle_workflow_run_success( outputs: Mapping[str, Any] | None = None, conversation_id: Optional[str] = None, trace_manager: Optional[TraceQueueManager] = None, + external_trace_id: Optional[str] = None, ) -> WorkflowExecution: workflow_execution = self._get_workflow_execution_or_raise_error(workflow_run_id) @@ -96,7 +97,7 @@ def handle_workflow_run_success( total_steps=total_steps, ) - self._add_trace_task_if_needed(trace_manager, workflow_execution, conversation_id) + self._add_trace_task_if_needed(trace_manager, workflow_execution, conversation_id, external_trace_id) self._workflow_execution_repository.save(workflow_execution) return workflow_execution @@ -111,6 +112,7 @@ def handle_workflow_run_partial_success( exceptions_count: int = 0, conversation_id: Optional[str] = None, trace_manager: Optional[TraceQueueManager] = None, + external_trace_id: Optional[str] = None, ) -> WorkflowExecution: execution = self._get_workflow_execution_or_raise_error(workflow_run_id) @@ -123,7 +125,7 @@ def handle_workflow_run_partial_success( exceptions_count=exceptions_count, ) - self._add_trace_task_if_needed(trace_manager, execution, conversation_id) + self._add_trace_task_if_needed(trace_manager, execution, conversation_id, external_trace_id) self._workflow_execution_repository.save(execution) return execution @@ -139,6 +141,7 @@ def handle_workflow_run_failed( conversation_id: Optional[str] = None, trace_manager: Optional[TraceQueueManager] = None, exceptions_count: int = 0, + external_trace_id: Optional[str] = None, ) -> WorkflowExecution: workflow_execution = self._get_workflow_execution_or_raise_error(workflow_run_id) now = naive_utc_now() @@ -154,7 +157,7 @@ def handle_workflow_run_failed( ) self._fail_running_node_executions(workflow_execution.id_, error_message, now) - self._add_trace_task_if_needed(trace_manager, workflow_execution, conversation_id) + self._add_trace_task_if_needed(trace_manager, workflow_execution, conversation_id, external_trace_id) self._workflow_execution_repository.save(workflow_execution) return workflow_execution @@ -312,6 +315,7 @@ def _add_trace_task_if_needed( trace_manager: Optional[TraceQueueManager], workflow_execution: WorkflowExecution, conversation_id: Optional[str], + external_trace_id: Optional[str], ) -> None: """Add trace task if trace manager is provided.""" if trace_manager: @@ -321,6 +325,7 @@ def _add_trace_task_if_needed( workflow_execution=workflow_execution, conversation_id=conversation_id, user_id=trace_manager.user_id, + external_trace_id=external_trace_id, ) ) diff --git a/api/tests/unit_tests/core/helper/test_trace_id_helper.py b/api/tests/unit_tests/core/helper/test_trace_id_helper.py new file mode 100644 index 00000000000000..27bfe1af054b12 --- /dev/null +++ b/api/tests/unit_tests/core/helper/test_trace_id_helper.py @@ -0,0 +1,86 @@ +import pytest + +from core.helper.trace_id_helper import extract_external_trace_id_from_args, get_external_trace_id, is_valid_trace_id + + +class DummyRequest: + def __init__(self, headers=None, args=None, json=None, is_json=False): + self.headers = headers or {} + self.args = args or {} + self.json = json + self.is_json = is_json + + +class TestTraceIdHelper: + """Test cases for trace_id_helper.py""" + + @pytest.mark.parametrize( + ("trace_id", "expected"), + [ + ("abc123", True), + ("A-B_C-123", True), + ("a" * 128, True), + ("", False), + ("a" * 129, False), + ("abc!@#", False), + ("空格", False), + ("with space", False), + ], + ) + def test_is_valid_trace_id(self, trace_id, expected): + """Test trace_id validation for various cases""" + assert is_valid_trace_id(trace_id) is expected + + def test_get_external_trace_id_from_header(self): + """Should extract valid trace_id from header""" + req = DummyRequest(headers={"X-Trace-Id": "abc123"}) + assert get_external_trace_id(req) == "abc123" + + def test_get_external_trace_id_from_args(self): + """Should extract valid trace_id from args if header missing""" + req = DummyRequest(args={"trace_id": "abc123"}) + assert get_external_trace_id(req) == "abc123" + + def test_get_external_trace_id_from_json(self): + """Should extract valid trace_id from JSON body if header and args missing""" + req = DummyRequest(is_json=True, json={"trace_id": "abc123"}) + assert get_external_trace_id(req) == "abc123" + + def test_get_external_trace_id_priority(self): + """Header > args > json priority""" + req = DummyRequest( + headers={"X-Trace-Id": "header_id"}, + args={"trace_id": "args_id"}, + is_json=True, + json={"trace_id": "json_id"}, + ) + assert get_external_trace_id(req) == "header_id" + req2 = DummyRequest(args={"trace_id": "args_id"}, is_json=True, json={"trace_id": "json_id"}) + assert get_external_trace_id(req2) == "args_id" + req3 = DummyRequest(is_json=True, json={"trace_id": "json_id"}) + assert get_external_trace_id(req3) == "json_id" + + @pytest.mark.parametrize( + "req", + [ + DummyRequest(headers={"X-Trace-Id": "!!!"}), + DummyRequest(args={"trace_id": "!!!"}), + DummyRequest(is_json=True, json={"trace_id": "!!!"}), + DummyRequest(), + ], + ) + def test_get_external_trace_id_invalid(self, req): + """Should return None for invalid or missing trace_id""" + assert get_external_trace_id(req) is None + + @pytest.mark.parametrize( + ("args", "expected"), + [ + ({"external_trace_id": "abc123"}, {"external_trace_id": "abc123"}), + ({"other": "value"}, {}), + ({}, {}), + ], + ) + def test_extract_external_trace_id_from_args(self, args, expected): + """Test extraction of external_trace_id from args mapping""" + assert extract_external_trace_id_from_args(args) == expected diff --git a/web/app/components/base/chat/chat/question.tsx b/web/app/components/base/chat/chat/question.tsx index 666a869a32ecc3..6630d9bb9d692f 100644 --- a/web/app/components/base/chat/chat/question.tsx +++ b/web/app/components/base/chat/chat/question.tsx @@ -117,7 +117,7 @@ const Question: FC = ({
{ diff --git a/web/app/components/develop/template/template_advanced_chat.en.mdx b/web/app/components/develop/template/template_advanced_chat.en.mdx index faecd3d1cd499b..1fb167bb9b4563 100644 --- a/web/app/components/develop/template/template_advanced_chat.en.mdx +++ b/web/app/components/develop/template/template_advanced_chat.en.mdx @@ -80,6 +80,12 @@ Chat applications support session persistence, allowing previous chat history to Auto-generate title, default is `true`. If set to `false`, can achieve async title generation by calling the conversation rename API and setting `auto_generate` to `true`. + + (Optional) Trace ID. Used for integration with existing business trace components to achieve end-to-end distributed tracing. If not provided, the system will automatically generate a trace_id. Supports the following three ways to pass, in order of priority:
+ - Header: via HTTP Header X-Trace-Id, highest priority.
+ - Query parameter: via URL query parameter trace_id.
+ - Request Body: via request body field trace_id (i.e., this field).
+
### Response diff --git a/web/app/components/develop/template/template_advanced_chat.ja.mdx b/web/app/components/develop/template/template_advanced_chat.ja.mdx index 5ce54a61d2bf6e..cdbe1a5dc8fae9 100644 --- a/web/app/components/develop/template/template_advanced_chat.ja.mdx +++ b/web/app/components/develop/template/template_advanced_chat.ja.mdx @@ -80,6 +80,12 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from タイトルを自動生成、デフォルトは`true`。 `false`に設定すると、会話のリネームAPIを呼び出し、`auto_generate`を`true`に設定することで非同期タイトル生成を実現できます。 + + (オプション)トレースID。既存の業務システムのトレースコンポーネントと連携し、エンドツーエンドの分散トレーシングを実現するために使用します。指定がない場合、システムが自動的に trace_id を生成します。以下の3つの方法で渡すことができ、優先順位は次のとおりです:
+ - Header:HTTPヘッダー X-Trace-Id で渡す(最優先)。
+ - クエリパラメータ:URLクエリパラメータ trace_id で渡す。
+ - リクエストボディ:リクエストボディの trace_id フィールドで渡す(本フィールド)。
+
### 応答 diff --git a/web/app/components/develop/template/template_advanced_chat.zh.mdx b/web/app/components/develop/template/template_advanced_chat.zh.mdx index 7a69ee60aac456..cb33416d78406a 100755 --- a/web/app/components/develop/template/template_advanced_chat.zh.mdx +++ b/web/app/components/develop/template/template_advanced_chat.zh.mdx @@ -78,6 +78,12 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' (选填)自动生成标题,默认 `true`。 若设置为 `false`,则可通过调用会话重命名接口并设置 `auto_generate` 为 `true` 实现异步生成标题。 + + (选填)链路追踪ID。适用于与业务系统已有的trace组件打通,实现端到端分布式追踪等场景。如果未指定,系统会自动生成trace_id。支持以下三种方式传递,具体优先级依次为:
+ - Header:通过 HTTP Header X-Trace-Id 传递,优先级最高。
+ - Query 参数:通过 URL 查询参数 trace_id 传递。
+ - Request Body:通过请求体字段 trace_id 传递(即本字段)。
+
### Response diff --git a/web/app/components/develop/template/template_chat.en.mdx b/web/app/components/develop/template/template_chat.en.mdx index c95471160e07c4..81620cee066d23 100644 --- a/web/app/components/develop/template/template_chat.en.mdx +++ b/web/app/components/develop/template/template_chat.en.mdx @@ -74,6 +74,12 @@ Chat applications support session persistence, allowing previous chat history to Auto-generate title, default is `true`. If set to `false`, can achieve async title generation by calling the conversation rename API and setting `auto_generate` to `true`. + + (Optional) Trace ID. Used for integration with existing business trace components to achieve end-to-end distributed tracing. If not provided, the system will automatically generate a trace_id. Supports the following three ways to pass, in order of priority:
+ - Header: via HTTP Header X-Trace-Id, highest priority.
+ - Query parameter: via URL query parameter trace_id.
+ - Request Body: via request body field trace_id (i.e., this field).
+
### Response diff --git a/web/app/components/develop/template/template_chat.ja.mdx b/web/app/components/develop/template/template_chat.ja.mdx index 8368326e407d0a..3287d68005c534 100644 --- a/web/app/components/develop/template/template_chat.ja.mdx +++ b/web/app/components/develop/template/template_chat.ja.mdx @@ -74,6 +74,12 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from タイトルを自動生成します。デフォルトは`true`です。 `false`に設定すると、会話のリネームAPIを呼び出し、`auto_generate`を`true`に設定することで非同期タイトル生成を実現できます。 + + (オプション)トレースID。既存の業務システムのトレースコンポーネントと連携し、エンドツーエンドの分散トレーシングを実現するために使用します。指定がない場合、システムが自動的に trace_id を生成します。以下の3つの方法で渡すことができ、優先順位は次のとおりです:
+ - Header:HTTPヘッダー X-Trace-Id で渡す(最優先)。
+ - クエリパラメータ:URLクエリパラメータ trace_id で渡す。
+ - リクエストボディ:リクエストボディの trace_id フィールドで渡す(本フィールド)。
+
### 応答 diff --git a/web/app/components/develop/template/template_chat.zh.mdx b/web/app/components/develop/template/template_chat.zh.mdx index 325470ac62d0fb..1926adc1ce2ee9 100644 --- a/web/app/components/develop/template/template_chat.zh.mdx +++ b/web/app/components/develop/template/template_chat.zh.mdx @@ -73,6 +73,12 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' (选填)自动生成标题,默认 `true`。 若设置为 `false`,则可通过调用会话重命名接口并设置 `auto_generate` 为 `true` 实现异步生成标题。 + + (选填)链路追踪ID。适用于与业务系统已有的trace组件打通,实现端到端分布式追踪等场景。如果未指定,系统会自动生成trace_id。支持以下三种方式传递,具体优先级依次为:
+ - Header:通过 HTTP Header X-Trace-Id 传递,优先级最高。
+ - Query 参数:通过 URL 查询参数 trace_id 传递。
+ - Request Body:通过请求体字段 trace_id 传递(即本字段)。
+
### Response diff --git a/web/app/components/develop/template/template_workflow.en.mdx b/web/app/components/develop/template/template_workflow.en.mdx index 77409c128455c9..7909c3e0819ba3 100644 --- a/web/app/components/develop/template/template_workflow.en.mdx +++ b/web/app/components/develop/template/template_workflow.en.mdx @@ -66,6 +66,12 @@ Workflow applications offers non-session support and is ideal for translation, a Should be uniquely defined by the developer within the application.
The user identifier should be consistent with the user passed in the message sending interface. The Service API does not share conversations created by the WebApp. + - `files` (array[object]) Optional + - `trace_id` (string) Optional + Trace ID. Used for integration with existing business trace components to achieve end-to-end distributed tracing. If not provided, the system will automatically generate a trace_id. Supports the following three ways to pass, in order of priority: + 1. Header: via HTTP Header `X-Trace-Id`, highest priority. + 2. Query parameter: via URL query parameter `trace_id`. + 3. Request Body: via request body field `trace_id` (i.e., this field). ### Response When `response_mode` is `blocking`, return a CompletionResponse object. diff --git a/web/app/components/develop/template/template_workflow.ja.mdx b/web/app/components/develop/template/template_workflow.ja.mdx index a83e21aef7f843..4231d83eb2c1d3 100644 --- a/web/app/components/develop/template/template_workflow.ja.mdx +++ b/web/app/components/develop/template/template_workflow.ja.mdx @@ -66,6 +66,11 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from ユーザー識別子、エンドユーザーのアイデンティティを定義するために使用されます。 アプリケーション内で開発者によって一意に定義される必要があります。 - `files` (array[object]) オプション + - `trace_id` (string) オプション + トレースID。既存の業務システムのトレースコンポーネントと連携し、エンドツーエンドの分散トレーシングを実現するために使用します。指定がない場合、システムが自動的に trace_id を生成します。以下の3つの方法で渡すことができ、優先順位は次のとおりです: + 1. Header:HTTPヘッダー `X-Trace-Id` で渡す(最優先)。 + 2. クエリパラメータ:URLクエリパラメータ `trace_id` で渡す。 + 3. リクエストボディ:リクエストボディの `trace_id` フィールドで渡す(本フィールド)。 ### 応答 diff --git a/web/app/components/develop/template/template_workflow.zh.mdx b/web/app/components/develop/template/template_workflow.zh.mdx index fc193de5da8482..dceefa48c35a97 100644 --- a/web/app/components/develop/template/template_workflow.zh.mdx +++ b/web/app/components/develop/template/template_workflow.zh.mdx @@ -60,7 +60,12 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等 - `user` (string) Required 用户标识,用于定义终端用户的身份,方便检索、统计。 由开发者定义规则,需保证用户标识在应用内唯一。API 无法访问 WebApp 创建的会话。 - + - `files` (array[object]) 可选 + - `trace_id` (string) Optional + 链路追踪ID。适用于与业务系统已有的trace组件打通,实现端到端分布式追踪等场景。如果未指定,系统将自动生成 `trace_id`。支持以下三种方式传递,具体优先级依次为: + 1. Header:推荐通过 HTTP Header `X-Trace-Id` 传递,优先级最高。 + 2. Query 参数:通过 URL 查询参数 `trace_id` 传递。 + 3. Request Body:通过请求体字段 `trace_id` 传递(即本字段)。 ### Response 当 `response_mode` 为 `blocking` 时,返回 CompletionResponse object。