feat(trace): support external trace id propagation (#22623)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
qfl
2025-07-22 15:17:43 +08:00
committed by GitHub
parent c987001a19
commit 841e53dbbe
25 changed files with 236 additions and 14 deletions

View File

@@ -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 @@ from core.errors.error import (
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 @@ class ChatApi(Resource):
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:

View File

@@ -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 @@ from core.errors.error import (
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 @@ class WorkflowRunApi(Resource):
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:

View File

@@ -23,6 +23,7 @@ from core.app.apps.message_based_app_generator import MessageBasedAppGenerator
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 @@ class AdvancedChatAppGenerator(MessageBasedAppGenerator):
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

View File

@@ -559,6 +559,7 @@ class AdvancedChatAppGenerateTaskPipeline:
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 @@ class AdvancedChatAppGenerateTaskPipeline:
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 @@ class AdvancedChatAppGenerateTaskPipeline:
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 @@ class AdvancedChatAppGenerateTaskPipeline:
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,

View File

@@ -22,6 +22,7 @@ from core.app.apps.workflow.generate_response_converter import WorkflowAppGenera
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 @@ class WorkflowAppGenerator(BaseAppGenerator):
)
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 @@ class WorkflowAppGenerator(BaseAppGenerator):
call_depth=call_depth,
trace_manager=trace_manager,
workflow_execution_id=workflow_run_id,
extras=extras,
)
contexts.plugin_tool_providers.set({})

View File

@@ -490,6 +490,7 @@ class WorkflowAppGenerateTaskPipeline:
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 @@ class WorkflowAppGenerateTaskPipeline:
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 @@ class WorkflowAppGenerateTaskPipeline:
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

View File

@@ -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 {}

View File

@@ -101,7 +101,8 @@ class AliyunDataTrace(BaseTraceInstance):
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)

View File

@@ -153,7 +153,8 @@ class ArizePhoenixDataTrace(BaseTraceInstance):
}
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,

View File

@@ -67,13 +67,14 @@ class LangFuseDataTrace(BaseTraceInstance):
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,

View File

@@ -65,7 +65,8 @@ class LangSmithDataTrace(BaseTraceInstance):
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 = (

View File

@@ -96,7 +96,8 @@ class OpikDataTrace(BaseTraceInstance):
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 @@ class OpikDataTrace(BaseTraceInstance):
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 = {

View File

@@ -520,6 +520,10 @@ class TraceTask:
"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,

View File

@@ -87,7 +87,8 @@ class WeaveDataTrace(BaseTraceInstance):
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()

View File

@@ -85,6 +85,7 @@ class WorkflowCycleManager:
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 @@ class WorkflowCycleManager:
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 @@ class WorkflowCycleManager:
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 @@ class WorkflowCycleManager:
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 @@ class WorkflowCycleManager:
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 @@ class WorkflowCycleManager:
)
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 @@ class WorkflowCycleManager:
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 @@ class WorkflowCycleManager:
workflow_execution=workflow_execution,
conversation_id=conversation_id,
user_id=trace_manager.user_id,
external_trace_id=external_trace_id,
)
)

View File

@@ -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

View File

@@ -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`.
</Property>
<Property name='trace_id' type='string' key='trace_id'>
(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:<br/>
- Header: via HTTP Header <code>X-Trace-Id</code>, highest priority.<br/>
- Query parameter: via URL query parameter <code>trace_id</code>.<br/>
- Request Body: via request body field <code>trace_id</code> (i.e., this field).<br/>
</Property>
</Properties>
### Response

View File

@@ -80,6 +80,12 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
タイトルを自動生成、デフォルトは`true`。
`false`に設定すると、会話のリネームAPIを呼び出し、`auto_generate`を`true`に設定することで非同期タイトル生成を実現できます。
</Property>
<Property name='trace_id' type='string' key='trace_id'>
オプショントレースID。既存の業務システムのトレースコンポーネントと連携し、エンドツーエンドの分散トレーシングを実現するために使用します。指定がない場合、システムが自動的に trace_id を生成します。以下の3つの方法で渡すことができ、優先順位は次のとおりです<br/>
- HeaderHTTPヘッダー <code>X-Trace-Id</code> で渡す(最優先)。<br/>
- クエリパラメータURLクエリパラメータ <code>trace_id</code> で渡す。<br/>
- リクエストボディ:リクエストボディの <code>trace_id</code> フィールドで渡す(本フィールド)。<br/>
</Property>
</Properties>
### 応答

View File

@@ -78,6 +78,12 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
<Property name='auto_generate_name' type='bool' key='auto_generate_name'>
(选填)自动生成标题,默认 `true`。 若设置为 `false`,则可通过调用会话重命名接口并设置 `auto_generate` 为 `true` 实现异步生成标题。
</Property>
<Property name='trace_id' type='string' key='trace_id'>
选填链路追踪ID。适用于与业务系统已有的trace组件打通实现端到端分布式追踪等场景。如果未指定系统会自动生成<code>trace_id</code>。支持以下三种方式传递,具体优先级依次为:<br/>
- Header通过 HTTP Header <code>X-Trace-Id</code> 传递,优先级最高。<br/>
- Query 参数:通过 URL 查询参数 <code>trace_id</code> 传递。<br/>
- Request Body通过请求体字段 <code>trace_id</code> 传递(即本字段)。<br/>
</Property>
</Properties>
### Response

View File

@@ -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`.
</Property>
<Property name='trace_id' type='string' key='trace_id'>
(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:<br/>
- Header: via HTTP Header <code>X-Trace-Id</code>, highest priority.<br/>
- Query parameter: via URL query parameter <code>trace_id</code>.<br/>
- Request Body: via request body field <code>trace_id</code> (i.e., this field).<br/>
</Property>
</Properties>
### Response

View File

@@ -74,6 +74,12 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
タイトルを自動生成します。デフォルトは`true`です。
`false`に設定すると、会話のリネームAPIを呼び出し、`auto_generate`を`true`に設定することで非同期タイトル生成を実現できます。
</Property>
<Property name='trace_id' type='string' key='trace_id'>
オプショントレースID。既存の業務システムのトレースコンポーネントと連携し、エンドツーエンドの分散トレーシングを実現するために使用します。指定がない場合、システムが自動的に trace_id を生成します。以下の3つの方法で渡すことができ、優先順位は次のとおりです<br/>
- HeaderHTTPヘッダー <code>X-Trace-Id</code> で渡す(最優先)。<br/>
- クエリパラメータURLクエリパラメータ <code>trace_id</code> で渡す。<br/>
- リクエストボディ:リクエストボディの <code>trace_id</code> フィールドで渡す(本フィールド)。<br/>
</Property>
</Properties>
### 応答

View File

@@ -73,6 +73,12 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
<Property name='auto_generate_name' type='bool' key='auto_generate_name'>
(选填)自动生成标题,默认 `true`。 若设置为 `false`,则可通过调用会话重命名接口并设置 `auto_generate` 为 `true` 实现异步生成标题。
</Property>
<Property name='trace_id' type='string' key='trace_id'>
选填链路追踪ID。适用于与业务系统已有的trace组件打通实现端到端分布式追踪等场景。如果未指定系统会自动生成<code>trace_id</code>。支持以下三种方式传递,具体优先级依次为:<br/>
- Header通过 HTTP Header <code>X-Trace-Id</code> 传递,优先级最高。<br/>
- Query 参数:通过 URL 查询参数 <code>trace_id</code> 传递。<br/>
- Request Body通过请求体字段 <code>trace_id</code> 传递(即本字段)。<br/>
</Property>
</Properties>
### Response

View File

@@ -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.
<br/>
<i>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.</i>
- `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.

View File

@@ -66,6 +66,11 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
ユーザー識別子、エンドユーザーのアイデンティティを定義するために使用されます。
アプリケーション内で開発者によって一意に定義される必要があります。
- `files` (array[object]) オプション
- `trace_id` (string) オプション
トレースID。既存の業務システムのトレースコンポーネントと連携し、エンドツーエンドの分散トレーシングを実現するために使用します。指定がない場合、システムが自動的に trace_id を生成します。以下の3つの方法で渡すことができ、優先順位は次のとおりです
1. HeaderHTTPヘッダー `X-Trace-Id` で渡す(最優先)。
2. クエリパラメータURLクエリパラメータ `trace_id` で渡す。
3. リクエストボディ:リクエストボディの `trace_id` フィールドで渡す(本フィールド)。
### 応答

View File

@@ -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。