Merge branch 'main' into feat/performance-optimization

pull/22810/head
twwu 7 months ago
commit a4219119d6

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -118,10 +118,21 @@ class TableStoreVector(BaseVector):
def search_by_vector(self, query_vector: list[float], **kwargs: Any) -> list[Document]:
top_k = kwargs.get("top_k", 4)
return self._search_by_vector(query_vector, top_k)
document_ids_filter = kwargs.get("document_ids_filter")
filtered_list = None
if document_ids_filter:
filtered_list = ["document_id=" + item for item in document_ids_filter]
score_threshold = float(kwargs.get("score_threshold") or 0.0)
return self._search_by_vector(query_vector, filtered_list, top_k, score_threshold)
def search_by_full_text(self, query: str, **kwargs: Any) -> list[Document]:
return self._search_by_full_text(query)
top_k = kwargs.get("top_k", 4)
document_ids_filter = kwargs.get("document_ids_filter")
filtered_list = None
if document_ids_filter:
filtered_list = ["document_id=" + item for item in document_ids_filter]
return self._search_by_full_text(query, filtered_list, top_k)
def delete(self) -> None:
self._delete_table_if_exist()
@ -230,32 +241,51 @@ class TableStoreVector(BaseVector):
primary_key = [("id", id)]
row = tablestore.Row(primary_key)
self._tablestore_client.delete_row(self._table_name, row, None)
logging.info("Tablestore delete row successfully. id:%s", id)
def _search_by_metadata(self, key: str, value: str) -> list[str]:
query = tablestore.SearchQuery(
tablestore.TermQuery(self._tags_field, str(key) + "=" + str(value)),
limit=100,
limit=1000,
get_total_count=False,
)
rows: list[str] = []
next_token = None
while True:
if next_token is not None:
query.next_token = next_token
search_response = self._tablestore_client.search(
table_name=self._table_name,
index_name=self._index_name,
search_query=query,
columns_to_get=tablestore.ColumnsToGet(
column_names=[Field.PRIMARY_KEY.value], return_type=tablestore.ColumnReturnType.SPECIFIED
),
)
search_response = self._tablestore_client.search(
table_name=self._table_name,
index_name=self._index_name,
search_query=query,
columns_to_get=tablestore.ColumnsToGet(return_type=tablestore.ColumnReturnType.ALL_FROM_INDEX),
)
if search_response is not None:
rows.extend([row[0][0][1] for row in search_response.rows])
return [row[0][0][1] for row in search_response.rows]
if search_response is None or search_response.next_token == b"":
break
else:
next_token = search_response.next_token
def _search_by_vector(self, query_vector: list[float], top_k: int) -> list[Document]:
ots_query = tablestore.KnnVectorQuery(
return rows
def _search_by_vector(
self, query_vector: list[float], document_ids_filter: list[str] | None, top_k: int, score_threshold: float
) -> list[Document]:
knn_vector_query = tablestore.KnnVectorQuery(
field_name=Field.VECTOR.value,
top_k=top_k,
float32_query_vector=query_vector,
)
if document_ids_filter:
knn_vector_query.filter = tablestore.TermsQuery(self._tags_field, document_ids_filter)
sort = tablestore.Sort(sorters=[tablestore.ScoreSort(sort_order=tablestore.SortOrder.DESC)])
search_query = tablestore.SearchQuery(ots_query, limit=top_k, get_total_count=False, sort=sort)
search_query = tablestore.SearchQuery(knn_vector_query, limit=top_k, get_total_count=False, sort=sort)
search_response = self._tablestore_client.search(
table_name=self._table_name,
@ -263,30 +293,32 @@ class TableStoreVector(BaseVector):
search_query=search_query,
columns_to_get=tablestore.ColumnsToGet(return_type=tablestore.ColumnReturnType.ALL_FROM_INDEX),
)
logging.info(
"Tablestore search successfully. request_id:%s",
search_response.request_id,
)
return self._to_query_result(search_response)
def _to_query_result(self, search_response: tablestore.SearchResponse) -> list[Document]:
documents = []
for row in search_response.rows:
documents.append(
Document(
page_content=row[1][2][1],
vector=json.loads(row[1][3][1]),
metadata=json.loads(row[1][0][1]),
for search_hit in search_response.search_hits:
if search_hit.score > score_threshold:
metadata = json.loads(search_hit.row[1][0][1])
metadata["score"] = search_hit.score
documents.append(
Document(
page_content=search_hit.row[1][2][1],
vector=json.loads(search_hit.row[1][3][1]),
metadata=metadata,
)
)
)
documents = sorted(documents, key=lambda x: x.metadata["score"] if x.metadata else 0, reverse=True)
return documents
def _search_by_full_text(self, query: str) -> list[Document]:
def _search_by_full_text(self, query: str, document_ids_filter: list[str] | None, top_k: int) -> list[Document]:
bool_query = tablestore.BoolQuery()
bool_query.must_queries.append(tablestore.MatchQuery(text=query, field_name=Field.CONTENT_KEY.value))
if document_ids_filter:
bool_query.filter_queries.append(tablestore.TermsQuery(self._tags_field, document_ids_filter))
search_query = tablestore.SearchQuery(
query=tablestore.MatchQuery(text=query, field_name=Field.CONTENT_KEY.value),
query=bool_query,
sort=tablestore.Sort(sorters=[tablestore.ScoreSort(sort_order=tablestore.SortOrder.DESC)]),
limit=100,
limit=top_k,
)
search_response = self._tablestore_client.search(
table_name=self._table_name,
@ -295,7 +327,16 @@ class TableStoreVector(BaseVector):
columns_to_get=tablestore.ColumnsToGet(return_type=tablestore.ColumnReturnType.ALL_FROM_INDEX),
)
return self._to_query_result(search_response)
documents = []
for search_hit in search_response.search_hits:
documents.append(
Document(
page_content=search_hit.row[1][2][1],
vector=json.loads(search_hit.row[1][3][1]),
metadata=json.loads(search_hit.row[1][0][1]),
)
)
return documents
class TableStoreVectorFactory(AbstractVectorFactory):

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

@ -45,8 +45,7 @@
line-height: 120%; /* 28.8px */
}
.button {
display: inline-block;
width: 480px;
display: block;
padding: 8px 12px;
color: white;
text-decoration: none;

@ -12,7 +12,7 @@
}
.container {
width: 504px;
height: 444px;
min-height: 444px;
margin: 40px auto;
padding: 0 48px;
background-color: #fcfcfd;
@ -31,8 +31,7 @@
height: auto;
}
.button {
display: inline-block;
width: 480px;
display: block;
padding: 8px 12px;
color: white;
text-decoration: none;

@ -28,11 +28,10 @@
.header img {
max-width: 63px;
height: auto;
min-height: auto;
}
.button {
display: inline-block;
width: 480px;
display: block;
padding: 8px 12px;
color: white;
text-decoration: none;

@ -12,7 +12,7 @@
}
.container {
width: 504px;
height: 444px;
min-height: 444px;
margin: 40px auto;
padding: 0 48px;
background-color: #fcfcfd;
@ -31,8 +31,7 @@
height: auto;
}
.button {
display: inline-block;
width: 480px;
display: block;
padding: 8px 12px;
color: white;
text-decoration: none;

@ -12,7 +12,7 @@
}
.container {
width: 504px;
height: 444px;
min-height: 444px;
margin: 40px auto;
padding: 0 48px;
background-color: #fcfcfd;
@ -31,8 +31,7 @@
height: auto;
}
.button {
display: inline-block;
width: 480px;
display: block;
padding: 8px 12px;
color: white;
text-decoration: none;

@ -1,4 +1,7 @@
import os
import uuid
import tablestore
from core.rag.datasource.vdb.tablestore.tablestore_vector import (
TableStoreConfig,
@ -6,6 +9,8 @@ from core.rag.datasource.vdb.tablestore.tablestore_vector import (
)
from tests.integration_tests.vdb.test_vector_store import (
AbstractVectorTest,
get_example_document,
get_example_text,
setup_mock_redis,
)
@ -29,6 +34,49 @@ class TableStoreVectorTest(AbstractVectorTest):
assert len(ids) == 1
assert ids[0] == self.example_doc_id
def create_vector(self):
self.vector.create(
texts=[get_example_document(doc_id=self.example_doc_id)],
embeddings=[self.example_embedding],
)
while True:
search_response = self.vector._tablestore_client.search(
table_name=self.vector._table_name,
index_name=self.vector._index_name,
search_query=tablestore.SearchQuery(query=tablestore.MatchAllQuery(), get_total_count=True, limit=0),
columns_to_get=tablestore.ColumnsToGet(return_type=tablestore.ColumnReturnType.ALL_FROM_INDEX),
)
if search_response.total_count == 1:
break
def search_by_vector(self):
super().search_by_vector()
docs = self.vector.search_by_vector(self.example_embedding, document_ids_filter=[self.example_doc_id])
assert len(docs) == 1
assert docs[0].metadata["doc_id"] == self.example_doc_id
assert docs[0].metadata["score"] > 0
docs = self.vector.search_by_vector(self.example_embedding, document_ids_filter=[str(uuid.uuid4())])
assert len(docs) == 0
def search_by_full_text(self):
super().search_by_full_text()
docs = self.vector.search_by_full_text(get_example_text(), document_ids_filter=[self.example_doc_id])
assert len(docs) == 1
assert docs[0].metadata["doc_id"] == self.example_doc_id
assert not hasattr(docs[0], "score")
docs = self.vector.search_by_full_text(get_example_text(), document_ids_filter=[str(uuid.uuid4())])
assert len(docs) == 0
def run_all_tests(self):
try:
self.vector.delete()
except Exception:
pass
return super().run_all_tests()
def test_tablestore_vector(setup_mock_redis):
TableStoreVectorTest().run_all_tests()

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

@ -28,8 +28,15 @@ import {
* Further refactoring candidates (custom block components not fitting general categories)
* are noted in their respective files if applicable.
*/
export type MarkdownProps = {
content: string
className?: string
customDisallowedElements?: string[]
customComponents?: Record<string, React.ComponentType<any>>
}
export function Markdown(props: { content: string; className?: string; customDisallowedElements?: string[] }) {
export const Markdown = (props: MarkdownProps) => {
const { customComponents = {} } = props
const latexContent = flow([
preprocessThinkTag,
preprocessLaTeX,
@ -78,6 +85,7 @@ export function Markdown(props: { content: string; className?: string; customDis
form: MarkdownForm,
script: ScriptBlock as any,
details: ThinkBlock,
...customComponents,
}}
>
{/* Markdown detect has problem. */}

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

@ -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>
### 応答

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

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

@ -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>
### 応答

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

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

@ -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` フィールドで渡す(本フィールド)。
### 応答

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

@ -1,7 +1,10 @@
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import { useCallback } from 'react'
import { useI18N } from '@/context/i18n'
dayjs.extend(relativeTime)
export const useFormatTimeFromNow = () => {
const { locale } = useI18N()
const formatTimeFromNow = useCallback((time: number) => {

@ -84,6 +84,16 @@ const translation = {
endpointDeleteTip: 'Endpunkt entfernen',
serviceOk: 'Service in Ordnung',
switchVersion: 'Version wechseln',
deprecation: {
reason: {
noMaintainer: 'kein Wartender',
ownershipTransferred: 'Eigentum übertragen',
businessAdjustments: 'Geschäftsanpassungen',
},
onlyReason: 'Dieses Plugin wurde aufgrund von {{deprecatedReason}} abgelehnt und wird nicht länger aktualisiert.',
fullMessage: 'Dieses Plugin wurde aufgrund von {{deprecatedReason}} eingestellt und wird nicht mehr aktualisiert. Bitte verwenden Sie stattdessen <CustomLink href=\'https://example.com/\'>{{-alternativePluginId}}</CustomLink>.',
noReason: 'Dieses Plugin wurde eingestellt und wird nicht mehr aktualisiert.',
},
},
debugInfo: {
title: 'Debuggen',
@ -237,6 +247,7 @@ const translation = {
useApiAuthDesc: 'Nachdem die Anmeldeinformationen konfiguriert wurden, können alle Mitglieder des Arbeitsbereichs dieses Tool beim Orchestrieren von Anwendungen verwenden.',
authRemoved: 'Die Authentifizierung wurde entfernt.',
},
deprecated: 'Abgelehnt',
}
export default translation

@ -690,6 +690,7 @@ const translation = {
license: {
expiring: 'Caduca en un día',
expiring_plural: 'Caducando en {{count}} días',
unlimited: 'Ilimitado',
},
pagination: {
perPage: 'Elementos por página',

@ -204,6 +204,7 @@ const translation = {
name: 'Nombre',
description: 'Puedes gestionar todos los metadatos en este conocimiento aquí. Las modificaciones se sincronizarán en todos los documentos.',
disabled: 'desactivar',
builtIn: 'Integrado',
},
documentMetadata: {
technicalParameters: 'Parámetros técnicos',

@ -84,6 +84,16 @@ const translation = {
endpointsDocLink: 'Ver el documento',
endpointsEmpty: 'Haga clic en el botón \'+\' para agregar un punto de conexión',
configureApp: 'Configurar la aplicación',
deprecation: {
reason: {
ownershipTransferred: 'propiedad transferida',
noMaintainer: 'sin mantenedor',
businessAdjustments: 'ajustes comerciales',
},
noReason: 'Este complemento ha sido descontinuado y ya no se actualizará.',
onlyReason: 'Este complemento ha sido desaprobado debido a {{deprecatedReason}} y ya no se actualizará.',
fullMessage: 'Este complemento ha sido descontinuado debido a {{deprecatedReason}}, y ya no será actualizado. Por favor, utilice <CustomLink href=\'https://example.com/\'>{{-alternativePluginId}}</CustomLink> en su lugar.',
},
},
debugInfo: {
title: 'Depuración',
@ -235,7 +245,9 @@ const translation = {
saveOnly: 'Guardar solo',
clientInfo: 'Como no se encontraron secretos de cliente del sistema para este proveedor de herramientas, se requiere configurarlo manualmente. Para redirect_uri, por favor utiliza',
oauthClientSettings: 'Configuración del cliente OAuth',
default: 'Predeterminado',
},
deprecated: 'Obsoleto',
}
export default translation

@ -475,10 +475,12 @@ const translation = {
options: {
disabled: {
subTitle: 'No habilitar el filtrado de metadatos',
title: 'Deshabilitado',
},
automatic: {
subTitle: 'Generar automáticamente condiciones de filtrado de metadatos basadas en la consulta del usuario',
desc: 'Generar automáticamente condiciones de filtrado de metadatos basadas en la variable de consulta',
title: 'Automático',
},
manual: {
title: 'Manual',
@ -898,6 +900,7 @@ const translation = {
error_other: '{{count}} Errores',
loopVariables: 'Variables de bucle',
variableName: 'Nombre de Variable',
input: 'Entrada',
},
},
tracing: {
@ -916,6 +919,7 @@ const translation = {
onlyShowNamedVersions: 'Solo muestra versiones nombradas',
empty: 'No se encontró un historial de versiones coincidente.',
reset: 'Restablecer filtro',
all: 'Todo',
},
editField: {
titleLengthLimit: 'El título no puede exceder {{limit}} caracteres',
@ -939,6 +943,7 @@ const translation = {
deletionTip: 'La eliminación es irreversible, por favor confirma.',
currentDraft: 'Borrador Actual',
editVersionInfo: 'Editar información de la versión',
latest: 'Último',
},
debug: {
noData: {
@ -951,6 +956,7 @@ const translation = {
stop: 'Detén la carrera',
normal: 'Inspeccionar Variable',
cached: 'Ver variables en caché',
clear: 'Claro',
},
envNode: 'Medio ambiente',
chatNode: 'Conversación',
@ -963,6 +969,7 @@ const translation = {
resetConversationVar: 'Restablecer la variable de conversación al valor predeterminado',
clearNode: 'Limpiar variable en caché',
emptyTip: 'Después de recorrer un nodo en el lienzo o ejecutar un nodo paso a paso, puedes ver el valor actual de la variable del nodo en Inspección de Variables.',
edited: 'Editado',
},
lastRunTab: 'Última ejecución',
settingsTab: 'Ajustes',

@ -84,6 +84,16 @@ const translation = {
endpointModalDesc: 'پس از پیکربندی، می توان از ویژگی های ارائه شده توسط افزونه از طریق نقاط پایانی API استفاده کرد.',
switchVersion: 'نسخه سوئیچ',
endpointDeleteContent: 'آیا می خواهید {{name}} را حذف کنید؟',
deprecation: {
reason: {
ownershipTransferred: 'مالکیت منتقل شد',
businessAdjustments: 'تنظیمات کسب و کار',
noMaintainer: 'بدون نگهدارنده',
},
noReason: 'این افزونه منسوخ شده است و دیگر به روز رسانی نخواهد شد.',
onlyReason: 'این افزونه به دلیل {{deprecatedReason}} منسوخ شده و دیگر به‌روزرسانی نخواهد شد.',
fullMessage: 'این افزونه به دلیل {{deprecatedReason}} منسوخ شده است و دیگر به‌روزرسانی نخواهد شد. لطفا به‌جای آن از <CustomLink href=\'https://example.com/\'>{{-alternativePluginId}}</CustomLink> استفاده کنید.',
},
},
debugInfo: {
title: 'اشکال زدایی',
@ -237,6 +247,7 @@ const translation = {
clientInfo: 'از آنجایی که هیچ راز مشتری سیستم برای این ارائه‌دهنده ابزار پیدا نشد، تنظیم دستی آن ضروری است، لطفاً برای redirect_uri از',
useApiAuthDesc: 'پس از پیکربندی اعتبارنامه‌ها، تمامی اعضای درون فضای کاری می‌توانند از این ابزار هنگام نظم‌دهی به برنامه‌ها استفاده کنند.',
},
deprecated: 'منسوخ شده',
}
export default translation

@ -84,6 +84,16 @@ const translation = {
endpointModalTitle: 'Configurer le point de terminaison',
serviceOk: 'Service OK',
endpointModalDesc: 'Une fois configuré, les fonctionnalités fournies par le plugin via les points de terminaison de lAPI peuvent être utilisées.',
deprecation: {
reason: {
ownershipTransferred: 'propriété transférée',
businessAdjustments: 'ajustements commerciaux',
noMaintainer: 'aucun mainteneur',
},
noReason: 'Ce plugin a été abandonné et ne sera plus mis à jour.',
onlyReason: 'Ce plugin a été déprécié en raison de {{deprecatedReason}} et ne sera plus mis à jour.',
fullMessage: 'Ce plugin a été déprécié en raison de {{deprecatedReason}}, et ne sera plus mis à jour. Veuillez utiliser <CustomLink href=\'https://example.com/\'>{{-alternativePluginId}}</CustomLink> à la place.',
},
},
debugInfo: {
title: 'Débogage',
@ -237,6 +247,7 @@ const translation = {
authorization: 'Autorisation',
useApi: 'Utilisez la clé API',
},
deprecated: 'Obsolète',
}
export default translation

@ -84,6 +84,16 @@ const translation = {
endpointModalTitle: 'एंडपॉइंट सेटअप करें',
strategyNum: '{{num}} {{रणनीति}} शामिल',
endpointsTip: 'यह प्लगइन एंडपॉइंट्स के माध्यम से विशिष्ट कार्यक्षमताएँ प्रदान करता है, और आप वर्तमान कार्यक्षेत्र के लिए कई एंडपॉइंट सेट कॉन्फ़िगर कर सकते हैं।',
deprecation: {
reason: {
noMaintainer: 'कोई देखभाल करने वाला नहीं',
ownershipTransferred: 'स्वामित्व स्थानांतरित किया गया',
businessAdjustments: 'व्यवसाय समायोजन',
},
noReason: 'यह प्लगइन अप्रचलित हो गया है और इसे अब अपडेट नहीं किया जाएगा।',
onlyReason: 'इस प्लगइन को {{deprecatedReason}} के कारण अमान्य कर दिया गया है और इसे अब अपडेट नहीं किया जाएगा।',
fullMessage: 'इस प्लगइन को {{deprecatedReason}} के कारण अमान्य कर दिया गया है, और इसे अब अपडेट नहीं किया जाएगा। कृपया इसके बजाय <CustomLink href=\'https://example.com/\'>{{-alternativePluginId}}</CustomLink> का उपयोग करें।',
},
},
debugInfo: {
viewDocs: 'दस्तावेज़ देखें',
@ -237,6 +247,7 @@ const translation = {
useApiAuthDesc: 'क्रेडेंशियल्स कॉन्फ़िगर करने के बाद, कार्यक्षेत्र के सभी सदस्यों को एप्लिकेशन को व्यवस्थित करते समय इस उपकरण का उपयोग करने की अनुमति होती है।',
clientInfo: 'चूंकि इस टूल प्रदाता के लिए कोई सिस्टम क्लाइंट रहस्य नहीं पाए गए हैं, इसलिए इसे मैन्युअल रूप से सेटअप करना आवश्यक है, कृपया redirect_uri का उपयोग करें',
},
deprecated: 'अनुशंसित नहीं',
}
export default translation

@ -232,6 +232,7 @@ const translation = {
structuredTip: 'Le Uscite Strutturate sono una funzione che garantisce che il modello generi sempre risposte che aderiscano al tuo Schema JSON fornito.',
notConfiguredTip: 'L\'output strutturato non è stato ancora configurato.',
modelNotSupportedTip: 'Il modello attuale non supporta questa funzione e viene automaticamente downgradato a iniezione di prompt.',
required: 'Necessario',
},
accessItemsDescription: {
anyone: 'Chiunque può accedere all\'app web',

@ -211,6 +211,7 @@ const translation = {
deleteContent: 'Sei sicuro di voler eliminare i metadati "{{name}}"?',
builtInDescription: 'I metadati incorporati vengono estratti e generati automaticamente. Devono essere abilitati prima dell\'uso e non possono essere modificati.',
description: 'Puoi gestire tutti i metadati in questa conoscenza qui. Le modifiche saranno sincronizzate con ogni documento.',
builtIn: 'Integrato',
},
documentMetadata: {
documentInformation: 'Informazioni sul documento',

@ -84,6 +84,16 @@ const translation = {
endpointDeleteTip: 'Rimuovi punto finale',
endpointsEmpty: 'Fare clic sul pulsante \'+\' per aggiungere un punto finale',
actionNum: '{{num}} {{azione}} INCLUSO',
deprecation: {
reason: {
noMaintainer: 'nessun manutentore',
ownershipTransferred: 'proprietà trasferita',
businessAdjustments: 'adeguamenti aziendali',
},
onlyReason: 'Questo plugin è stato deprecato a causa di {{deprecatedReason}} e non verrà più aggiornato.',
fullMessage: 'Questo plugin è stato deprecato a causa di {{deprecatedReason}} e non verrà più aggiornato. Si prega di utilizzare <CustomLink href=\'https://example.com/\'>{{-alternativePluginId}}</CustomLink> invece.',
noReason: 'Questo plugin è stato deprecato e non sarà più aggiornato.',
},
},
debugInfo: {
title: 'Debug',
@ -237,6 +247,7 @@ const translation = {
useApiAuth: 'Configurazione dell\'autorizzazione della chiave API',
clientInfo: 'Poiché non sono stati trovati segreti client di sistema per questo fornitore di strumenti, è necessario configurarlo manualmente. Per redirect_uri, si prega di utilizzare',
},
deprecated: 'Deprecato',
}
export default translation

@ -497,6 +497,7 @@ const translation = {
automatic: {
subTitle: 'Genera automaticamente condizioni di filtraggio dei metadati in base alla query dell\'utente',
desc: 'Genera automaticamente condizioni di filtraggio dei metadati basate sulla variabile di query',
title: 'Automatico',
},
manual: {
title: 'Manuale',
@ -611,6 +612,7 @@ const translation = {
'exists': 'Esiste',
'not exists': 'non esiste',
'after': 'dopo',
'before': 'prima',
},
enterValue: 'Inserisci valore',
addCondition: 'Aggiungi Condizione',

@ -84,6 +84,16 @@ const translation = {
endpoints: '끝점',
serviceOk: '서비스 정상',
endpointDisableTip: '엔드포인트 비활성화',
deprecation: {
reason: {
ownershipTransferred: '소유권 이전',
businessAdjustments: '사업 조정',
noMaintainer: '유지보수자 없음',
},
noReason: '이 플러그인은 더 이상 지원되지 않으며 업데이트되지 않을 것입니다.',
onlyReason: '이 플러그인은 {{deprecatedReason}}로 인해 사용 중단되었으며 더 이상 업데이트되지 않습니다.',
fullMessage: '이 플러그인은 {{deprecatedReason}}로 인해 사용 중단되었으며 더 이상 업데이트되지 않습니다. 대신 <CustomLink href=\'https://example.com/\'>{{-alternativePluginId}}</CustomLink>를 사용하십시오.',
},
},
debugInfo: {
title: '디버깅',
@ -237,6 +247,7 @@ const translation = {
useApiAuthDesc: '자격증명을 구성한 후에는 작업 공간 내의 모든 구성원이 애플리케이션을 조정할 때 이 도구를 사용할 수 있습니다.',
clientInfo: '이 도구 공급자에 대한 시스템 클라이언트 비밀이 발견되지 않았으므로 수동으로 설정해야 하며, redirect_uri는 다음을 사용하십시오.',
},
deprecated: '사용 중단됨',
}
export default translation

@ -84,6 +84,16 @@ const translation = {
endpointDeleteContent: 'Czy chcesz usunąć {{name}}?',
endpointsTip: 'Ta wtyczka zapewnia określone funkcje za pośrednictwem punktów końcowych i można skonfigurować wiele zestawów punktów końcowych dla bieżącego obszaru roboczego.',
modelNum: '{{liczba}} MODELE W ZESTAWIE',
deprecation: {
reason: {
businessAdjustments: 'dostosowania biznesowe',
ownershipTransferred: 'własność przekazana',
noMaintainer: 'brak opiekuna',
},
onlyReason: 'Ten plugin został wycofany z użycia z powodu {{deprecatedReason}} i nie będzie już aktualizowany.',
noReason: 'Ten wtyczka została przestarzała i nie będzie dłużej aktualizowana.',
fullMessage: 'Ten plugin został wycofany z użycia z powodu {{deprecatedReason}} i nie będzie już aktualizowany. Proszę użyć zamiast tego <CustomLink href=\'https://example.com/\'>{{-alternativePluginId}}</CustomLink>.',
},
},
debugInfo: {
viewDocs: 'Wyświetlanie dokumentów',
@ -237,6 +247,7 @@ const translation = {
useApiAuthDesc: 'Po skonfigurowaniu poświadczeń wszyscy członkowie w przestrzeni roboczej mogą korzystać z tego narzędzia podczas orkiestracji aplikacji.',
clientInfo: 'Ponieważ nie znaleziono tajemnic klientów systemu dla tego dostawcy narzędzi, wymagane jest ręczne skonfigurowanie, dla redirect_uri proszę użyć',
},
deprecated: 'Nieaktualny',
}
export default translation

@ -84,6 +84,16 @@ const translation = {
configureTool: 'Ferramenta de configuração',
endpointsDocLink: 'Veja o documento',
endpointModalTitle: 'Ponto de extremidade de configuração',
deprecation: {
reason: {
businessAdjustments: 'ajustes de negócios',
ownershipTransferred: 'propriedade transferida',
noMaintainer: 'sem mantenedor',
},
onlyReason: 'Este plugin foi descontinuado devido a {{deprecatedReason}} e não será mais atualizado.',
noReason: 'Este plugin foi descontinuado e não será mais atualizado.',
fullMessage: 'Este plugin foi descontinuado devido a {{deprecatedReason}}, e não receberá mais atualizações. Por favor, use <CustomLink href=\'https://example.com/\'>{{-alternativePluginId}}</CustomLink> em vez disso.',
},
},
debugInfo: {
title: 'Depuração',
@ -237,6 +247,7 @@ const translation = {
useApiAuthDesc: 'Após configurar as credenciais, todos os membros dentro do espaço de trabalho podem usar esta ferramenta ao orquestrar aplicações.',
clientInfo: 'Como não foram encontrados segredos de cliente do sistema para este provedor de ferramentas, é necessário configurá-lo manualmente. Para redirect_uri, use',
},
deprecated: 'Obsoleto',
}
export default translation

@ -84,6 +84,16 @@ const translation = {
endpointModalDesc: 'Odată configurate, pot fi utilizate funcțiile furnizate de plugin prin intermediul punctelor finale API.',
modelNum: '{{num}} MODELE INCLUSE',
configureModel: 'Configurarea modelului',
deprecation: {
reason: {
businessAdjustments: 'ajustări de afaceri',
noMaintainer: 'fără întreținător',
ownershipTransferred: 'proprietatea transferată',
},
noReason: 'Acest plugin a fost declarat învechit și nu va mai fi actualizat.',
onlyReason: 'Acest plugin a fost depreciat din cauza {{deprecatedReason}} și nu va mai fi actualizat.',
fullMessage: 'Acest plugin a fost declarat învechit din cauza {{deprecatedReason}}, și nu va mai fi actualizat. Vă rugăm să folosiți în schimb <CustomLink href=\'https://example.com/\'>{{-alternativePluginId}}</CustomLink>.',
},
},
debugInfo: {
viewDocs: 'Vizualizați documentele',
@ -237,6 +247,7 @@ const translation = {
useApiAuthDesc: 'După configurarea acreditivelor, toți membrii din spațiul de lucru pot folosi acest instrument atunci când orchestran aplicații.',
clientInfo: 'Deoarece nu s-au găsit secretele clientului sistemului pentru acest furnizor de instrumente, este necesară configurarea manuală; pentru redirect_uri, vă rugăm să folosiți',
},
deprecated: 'Încetat de a mai fi utilizat',
}
export default translation

@ -84,6 +84,16 @@ const translation = {
endpointsEmpty: 'Нажмите кнопку «+», чтобы добавить конечную точку',
switchVersion: 'Версия для переключателя',
endpointsDocLink: 'Посмотреть документ',
deprecation: {
reason: {
businessAdjustments: 'бизнес-правки',
ownershipTransferred: 'передача права собственности',
noMaintainer: 'нет сопровождающего',
},
noReason: 'Этот плагин был устаревшим и больше не будет обновляться.',
onlyReason: 'Этот плагин был устаревшим из-за {{deprecatedReason}} и больше не будет обновляться.',
fullMessage: 'Этот плагин больше не поддерживается по причине {{deprecatedReason}} и больше не будет обновляться. Пожалуйста, используйте <CustomLink href=\'https://example.com/\'>{{-alternativePluginId}}</CustomLink> вместо этого.',
},
},
debugInfo: {
title: 'Отладка',
@ -237,6 +247,7 @@ const translation = {
useApiAuthDesc: 'После настройки учетных данных все члены рабочей области могут использовать этот инструмент при оркестрации приложений.',
clientInfo: 'Поскольку не найдены секреты клиентской системы для этого поставщика инструментов, необходимо настроить его вручную, для redirect_uri, пожалуйста, используйте',
},
deprecated: 'Устаревший',
}
export default translation

@ -87,6 +87,16 @@ const translation = {
endpointsTip: 'Ta vtičnik zagotavlja specifične funkcionalnosti preko končnih točk, prav tako pa lahko konfigurirate več nizov končnih točk za trenutno delovno okolje.',
endpointModalDesc: 'Ko je konfiguriran, se lahko uporabljajo funkcije, ki jih vtičnik zagotavlja prek API končnih točk.',
endpointsEmpty: 'Kliknite gumb \' \' za dodajanje končne točke',
deprecation: {
reason: {
businessAdjustments: 'poslovne prilagoditve',
noMaintainer: 'brez vzdrževalca',
ownershipTransferred: 'lastništvo preneseno',
},
onlyReason: 'Ta vtičnik je bil opuščen zaradi {{deprecatedReason}} in ne bo več posodobljen.',
noReason: 'Ta vtičnik je bil ukinjen in ne bo več posodabljan.',
fullMessage: 'Ta vtičnik je bil ukinjen zaradi {{deprecatedReason}}, in ne bo več posodobljen. Namesto tega uporabite <CustomLink href=\'https://example.com/\'>{{-alternativePluginId}}</CustomLink>.',
},
},
debugInfo: {
viewDocs: 'Oglejte si dokumente',
@ -237,6 +247,7 @@ const translation = {
clientInfo: 'Ker za tega ponudnika orodij niso bili najdeni klientski skrivnosti sistema, je potrebna ročna nastavitev, za redirect_uri prosimo uporabite',
useApiAuthDesc: 'Po konfiguraciji poverilnic lahko vsi člani v delovnem prostoru uporabljajo to orodje pri orkestraciji aplikacij.',
},
deprecated: 'Zastaran',
}
export default translation

@ -84,6 +84,16 @@ const translation = {
endpointsTip: 'ปลั๊กอินนี้มีฟังก์ชันเฉพาะผ่านปลายทาง และคุณสามารถกําหนดค่าชุดปลายทางหลายชุดสําหรับพื้นที่ทํางานปัจจุบันได้',
endpointsEmpty: 'คลิกปุ่ม \'+\' เพื่อเพิ่มปลายทาง',
serviceOk: 'บริการตกลง',
deprecation: {
reason: {
ownershipTransferred: 'การโอนความเป็นเจ้าของ',
businessAdjustments: 'การปรับเปลี่ยนธุรกิจ',
noMaintainer: 'ไม่มีผู้ดูแล',
},
onlyReason: 'ปลั๊กอินนี้ถูกเลิกใช้เนื่องจาก {{deprecatedReason}} และจะไม่มีการอัปเดตอีกต่อไป.',
noReason: 'ปลั๊กอินนี้ได้ถูกยกเลิกใช้งานและจะไม่มีการอัปเดตอีกต่อไป.',
fullMessage: 'ปลั๊กอินนี้ถูกยกเลิกการใช้งานเนื่องจาก {{เหตุผลที่ถูกยกเลิก}} และจะไม่มีการอัปเดตอีกต่อไป กรุณาใช้ <CustomLink href=\'https://example.com/\'>{{-alternativePluginId}}</CustomLink> แทน.',
},
},
debugInfo: {
viewDocs: 'ดูเอกสาร',
@ -237,6 +247,7 @@ const translation = {
useApiAuthDesc: 'หลังจากตั้งค่าข้อมูลประจำตัวแล้ว สมาชิกทุกคนภายในพื้นที่ทำงานสามารถใช้เครื่องมือนี้เมื่อจัดการแอปพลิเคชันได้',
clientInfo: 'เนื่องจากไม่พบความลับของลูกค้าสำหรับผู้ให้บริการเครื่องมือนี้ จำเป็นต้องตั้งค่าแบบแมนนวล สำหรับ redirect_uri กรุณาใช้',
},
deprecated: 'เลิกใช้',
}
export default translation

@ -84,6 +84,16 @@ const translation = {
modelNum: '{{sayı}} DAHİL OLAN MODELLER',
endpointDisableTip: 'Uç Noktayı Devre Dışı Bırak',
serviceOk: 'Servis Tamam',
deprecation: {
reason: {
noMaintainer: 'bakımcı yok',
ownershipTransferred: 'mülkiyet devredildi',
businessAdjustments: 'iş ayarlamaları',
},
noReason: 'Bu eklenti kullanımdan kaldırıldı ve artık güncellenmeyecek.',
onlyReason: 'Bu eklenti {{deprecatedReason}} nedeniyle kullanımdan kaldırılmıştır ve artık güncellenmeyecektir.',
fullMessage: 'Bu eklenti {{deprecatedReason}} nedeniyle kullanım dışı bırakılmıştır ve artık güncellenmeyecek. Lütfen bunun yerine <CustomLink href=\'https://example.com/\'>{{-alternativePluginId}}</CustomLink>\'i kullanın.',
},
},
debugInfo: {
title: 'Hata ayıklama',
@ -237,6 +247,7 @@ const translation = {
saveAndAuth: 'Kaydet ve Yetkilendir',
clientInfo: 'Bu araç sağlayıcı için sistem istemci gizlilikleri bulunmadığından, manuel olarak ayar yapılması gerekmektedir. redirect_uri için lütfen şu adresi kullanın',
},
deprecated: 'Kaldırılmış',
}
export default translation

@ -84,6 +84,16 @@ const translation = {
endpointModalDesc: 'Після налаштування можна використовувати функції, що надаються плагіном через кінцеві точки API.',
configureTool: 'Інструмент налаштування',
serviceOk: 'Сервіс працює',
deprecation: {
reason: {
ownershipTransferred: 'право власності передано',
businessAdjustments: 'бізнесові корективи',
noMaintainer: 'немає супроводжувача',
},
noReason: 'Цей плагін було застаріло, і він більше не буде оновлюватися.',
onlyReason: 'Цей плагін було знято з підтримки через {{deprecatedReason}} і більше не буде оновлюватися.',
fullMessage: 'Цей плагін був застарілий через {{deprecatedReason}}, і більше не буде оновлюватися. Будь ласка, використовуйте <CustomLink href=\'https://example.com/\'>{{-alternativePluginId}}</CustomLink> замість цього.',
},
},
debugInfo: {
title: 'Налагодження',
@ -237,6 +247,7 @@ const translation = {
clientInfo: 'Оскільки не знайдено жодних секретів клієнта системи для цього постачальника інструментів, потрібно налаштувати його вручну; для redirect_uri, будь ласка, використовуйте',
useApiAuthDesc: 'Після налаштування облікових даних усі учасники робочого простору можуть використовувати цей інструмент під час оркестрації додатків.',
},
deprecated: 'Застарілий',
}
export default translation

@ -84,6 +84,16 @@ const translation = {
endpointDeleteContent: 'Bạn có muốn xóa {{name}} không?',
endpointModalTitle: 'Điểm cuối thiết lập',
disabled: 'Tàn tật',
deprecation: {
reason: {
noMaintainer: 'không có người bảo trì',
ownershipTransferred: 'quyền sở hữu được chuyển nhượng',
businessAdjustments: 'điều chỉnh kinh doanh',
},
noReason: 'Plugin này đã bị loại bỏ và sẽ không còn được cập nhật.',
onlyReason: 'Plugin này đã bị ngừng hỗ trợ do {{deprecatedReason}} và sẽ không còn được cập nhật nữa.',
fullMessage: 'Plugin này đã bị ngừng sử dụng do {{deprecatedReason}}, và sẽ không còn được cập nhật nữa. Vui lòng sử dụng <CustomLink href=\'https://example.com/\'>{{-alternativePluginId}}</CustomLink> thay thế.',
},
},
debugInfo: {
title: 'Gỡ lỗi',
@ -237,6 +247,7 @@ const translation = {
useApiAuthDesc: 'Sau khi cấu hình thông tin xác thực, tất cả các thành viên trong không gian làm việc có thể sử dụng công cụ này khi điều phối các ứng dụng.',
clientInfo: 'Vì không tìm thấy bí mật khách hàng hệ thống cho nhà cung cấp công cụ này, cần thiết lập thủ công, đối với redirect_uri, vui lòng sử dụng',
},
deprecated: 'Đã bị ngưng sử dụng',
}
export default translation

@ -84,6 +84,16 @@ const translation = {
configureModel: '配置模型',
endpointModalTitle: '設置終端節點',
endpointsDocLink: '查看文件',
deprecation: {
reason: {
businessAdjustments: '業務調整',
ownershipTransferred: '所有權轉移',
noMaintainer: '沒有維護者',
},
noReason: '此插件已被廢棄,將不再進行更新。',
onlyReason: '此插件因為 {{deprecatedReason}} 而被棄用,將不再更新。',
fullMessage: '由於 {{deprecatedReason}},此插件已被棄用,將不再更新。請改用 <CustomLink href=\'https://example.com/\'>{{-alternativePluginId}}</CustomLink>。',
},
},
debugInfo: {
viewDocs: '查看文件',
@ -237,6 +247,7 @@ const translation = {
clientInfo: '由於未找到此工具提供者的系統客戶端秘密,因此需要手動設置,對於 redirect_uri請使用',
useApiAuthDesc: '配置完憑證後,工作區內的所有成員在協調應用程式時都可以使用此工具。',
},
deprecated: '不推薦使用的',
}
export default translation

Loading…
Cancel
Save