mirror of
https://github.com/langgenius/dify.git
synced 2024-11-16 11:42:29 +08:00
Compare commits
30 Commits
e7fb51d5a2
...
85deb8b31c
Author | SHA1 | Date | |
---|---|---|---|
|
85deb8b31c | ||
|
d05fee1182 | ||
|
abacc3768f | ||
|
e31358219c | ||
|
4e360ec19a | ||
|
f68d6bd5e2 | ||
|
b860a893c8 | ||
|
0354c7813e | ||
|
94794d892e | ||
|
fb94d0b7cf | ||
|
02c39b2631 | ||
|
87f78ff582 | ||
|
6872b32c7d | ||
|
97fab7649b | ||
|
9f0f82cb1c | ||
|
ef08abafdf | ||
|
d6c9ab8554 | ||
|
bab989e3b3 | ||
|
ddc86503dc | ||
|
abad35f700 | ||
|
620b0e69f5 | ||
|
71cf4c7dbf | ||
|
47e8a5d4d1 | ||
|
93bbb194f2 | ||
|
2106fc5266 | ||
|
229b146525 | ||
|
d9fa6f79be | ||
|
4f89214d89 | ||
|
1fdaea29aa | ||
|
1397d0000d |
|
@ -27,7 +27,6 @@ class DifyConfig(
|
|||
# read from dotenv format config file
|
||||
env_file=".env",
|
||||
env_file_encoding="utf-8",
|
||||
frozen=True,
|
||||
# ignore extra attributes
|
||||
extra="ignore",
|
||||
)
|
||||
|
|
|
@ -11,7 +11,7 @@ from core.provider_manager import ProviderManager
|
|||
|
||||
class ModelConfigConverter:
|
||||
@classmethod
|
||||
def convert(cls, app_config: EasyUIBasedAppConfig, skip_check: bool = False) -> ModelConfigWithCredentialsEntity:
|
||||
def convert(cls, app_config: EasyUIBasedAppConfig) -> ModelConfigWithCredentialsEntity:
|
||||
"""
|
||||
Convert app model config dict to entity.
|
||||
:param app_config: app config
|
||||
|
@ -38,12 +38,8 @@ class ModelConfigConverter:
|
|||
)
|
||||
|
||||
if model_credentials is None:
|
||||
if not skip_check:
|
||||
raise ProviderTokenNotInitError(f"Model {model_name} credentials is not initialized.")
|
||||
else:
|
||||
model_credentials = {}
|
||||
|
||||
if not skip_check:
|
||||
# check model
|
||||
provider_model = provider_model_bundle.configuration.get_provider_model(
|
||||
model=model_config.model, model_type=ModelType.LLM
|
||||
|
@ -76,7 +72,7 @@ class ModelConfigConverter:
|
|||
|
||||
model_schema = model_type_instance.get_model_schema(model_config.model, model_credentials)
|
||||
|
||||
if not skip_check and not model_schema:
|
||||
if not model_schema:
|
||||
raise ValueError(f"Model {model_name} not exist.")
|
||||
|
||||
return ModelConfigWithCredentialsEntity(
|
||||
|
|
|
@ -217,6 +217,7 @@ class WorkflowCycleManage:
|
|||
).total_seconds()
|
||||
db.session.commit()
|
||||
|
||||
db.session.add(workflow_run)
|
||||
db.session.refresh(workflow_run)
|
||||
db.session.close()
|
||||
|
||||
|
|
|
@ -74,6 +74,8 @@ def to_prompt_message_content(
|
|||
data = _to_url(f)
|
||||
else:
|
||||
data = _to_base64_data_string(f)
|
||||
if f.extension is None:
|
||||
raise ValueError("Missing file extension")
|
||||
return VideoPromptMessageContent(data=data, format=f.extension.lstrip("."))
|
||||
case _:
|
||||
raise ValueError("file type f.type is not supported")
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from collections.abc import Sequence
|
||||
from typing import Optional
|
||||
|
||||
from core.app.app_config.features.file_upload.manager import FileUploadConfigManager
|
||||
|
@ -27,7 +28,7 @@ class TokenBufferMemory:
|
|||
|
||||
def get_history_prompt_messages(
|
||||
self, max_token_limit: int = 2000, message_limit: Optional[int] = None
|
||||
) -> list[PromptMessage]:
|
||||
) -> Sequence[PromptMessage]:
|
||||
"""
|
||||
Get history prompt messages.
|
||||
:param max_token_limit: max token limit
|
||||
|
|
|
@ -100,10 +100,10 @@ class ModelInstance:
|
|||
|
||||
def invoke_llm(
|
||||
self,
|
||||
prompt_messages: list[PromptMessage],
|
||||
prompt_messages: Sequence[PromptMessage],
|
||||
model_parameters: Optional[dict] = None,
|
||||
tools: Sequence[PromptMessageTool] | None = None,
|
||||
stop: Optional[list[str]] = None,
|
||||
stop: Optional[Sequence[str]] = None,
|
||||
stream: bool = True,
|
||||
user: Optional[str] = None,
|
||||
callbacks: Optional[list[Callback]] = None,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from collections.abc import Sequence
|
||||
from typing import Optional
|
||||
|
||||
from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk
|
||||
|
@ -31,7 +32,7 @@ class Callback(ABC):
|
|||
prompt_messages: list[PromptMessage],
|
||||
model_parameters: dict,
|
||||
tools: Optional[list[PromptMessageTool]] = None,
|
||||
stop: Optional[list[str]] = None,
|
||||
stop: Optional[Sequence[str]] = None,
|
||||
stream: bool = True,
|
||||
user: Optional[str] = None,
|
||||
) -> None:
|
||||
|
@ -60,7 +61,7 @@ class Callback(ABC):
|
|||
prompt_messages: list[PromptMessage],
|
||||
model_parameters: dict,
|
||||
tools: Optional[list[PromptMessageTool]] = None,
|
||||
stop: Optional[list[str]] = None,
|
||||
stop: Optional[Sequence[str]] = None,
|
||||
stream: bool = True,
|
||||
user: Optional[str] = None,
|
||||
):
|
||||
|
@ -90,7 +91,7 @@ class Callback(ABC):
|
|||
prompt_messages: list[PromptMessage],
|
||||
model_parameters: dict,
|
||||
tools: Optional[list[PromptMessageTool]] = None,
|
||||
stop: Optional[list[str]] = None,
|
||||
stop: Optional[Sequence[str]] = None,
|
||||
stream: bool = True,
|
||||
user: Optional[str] = None,
|
||||
) -> None:
|
||||
|
@ -120,7 +121,7 @@ class Callback(ABC):
|
|||
prompt_messages: list[PromptMessage],
|
||||
model_parameters: dict,
|
||||
tools: Optional[list[PromptMessageTool]] = None,
|
||||
stop: Optional[list[str]] = None,
|
||||
stop: Optional[Sequence[str]] = None,
|
||||
stream: bool = True,
|
||||
user: Optional[str] = None,
|
||||
) -> None:
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from abc import ABC
|
||||
from collections.abc import Sequence
|
||||
from enum import Enum
|
||||
from typing import Optional
|
||||
|
||||
|
@ -57,6 +58,7 @@ class PromptMessageContentType(Enum):
|
|||
IMAGE = "image"
|
||||
AUDIO = "audio"
|
||||
VIDEO = "video"
|
||||
DOCUMENT = "document"
|
||||
|
||||
|
||||
class PromptMessageContent(BaseModel):
|
||||
|
@ -107,7 +109,7 @@ class PromptMessage(ABC, BaseModel):
|
|||
"""
|
||||
|
||||
role: PromptMessageRole
|
||||
content: Optional[str | list[PromptMessageContent]] = None
|
||||
content: Optional[str | Sequence[PromptMessageContent]] = None
|
||||
name: Optional[str] = None
|
||||
|
||||
def is_empty(self) -> bool:
|
||||
|
|
|
@ -87,6 +87,9 @@ class ModelFeature(Enum):
|
|||
AGENT_THOUGHT = "agent-thought"
|
||||
VISION = "vision"
|
||||
STREAM_TOOL_CALL = "stream-tool-call"
|
||||
DOCUMENT = "document"
|
||||
VIDEO = "video"
|
||||
AUDIO = "audio"
|
||||
|
||||
|
||||
class DefaultParameterName(str, Enum):
|
||||
|
|
|
@ -2,7 +2,7 @@ import logging
|
|||
import re
|
||||
import time
|
||||
from abc import abstractmethod
|
||||
from collections.abc import Generator, Mapping
|
||||
from collections.abc import Generator, Mapping, Sequence
|
||||
from typing import Optional, Union
|
||||
|
||||
from pydantic import ConfigDict
|
||||
|
@ -48,7 +48,7 @@ class LargeLanguageModel(AIModel):
|
|||
prompt_messages: list[PromptMessage],
|
||||
model_parameters: Optional[dict] = None,
|
||||
tools: Optional[list[PromptMessageTool]] = None,
|
||||
stop: Optional[list[str]] = None,
|
||||
stop: Optional[Sequence[str]] = None,
|
||||
stream: bool = True,
|
||||
user: Optional[str] = None,
|
||||
callbacks: Optional[list[Callback]] = None,
|
||||
|
@ -169,7 +169,7 @@ class LargeLanguageModel(AIModel):
|
|||
prompt_messages: list[PromptMessage],
|
||||
model_parameters: dict,
|
||||
tools: Optional[list[PromptMessageTool]] = None,
|
||||
stop: Optional[list[str]] = None,
|
||||
stop: Optional[Sequence[str]] = None,
|
||||
stream: bool = True,
|
||||
user: Optional[str] = None,
|
||||
callbacks: Optional[list[Callback]] = None,
|
||||
|
@ -212,7 +212,7 @@ if you are not sure about the structure.
|
|||
)
|
||||
|
||||
model_parameters.pop("response_format")
|
||||
stop = stop or []
|
||||
stop = list(stop) if stop is not None else []
|
||||
stop.extend(["\n```", "```\n"])
|
||||
block_prompts = block_prompts.replace("{{block}}", code_block)
|
||||
|
||||
|
@ -408,7 +408,7 @@ if you are not sure about the structure.
|
|||
prompt_messages: list[PromptMessage],
|
||||
model_parameters: dict,
|
||||
tools: Optional[list[PromptMessageTool]] = None,
|
||||
stop: Optional[list[str]] = None,
|
||||
stop: Optional[Sequence[str]] = None,
|
||||
stream: bool = True,
|
||||
user: Optional[str] = None,
|
||||
callbacks: Optional[list[Callback]] = None,
|
||||
|
@ -479,7 +479,7 @@ if you are not sure about the structure.
|
|||
prompt_messages: list[PromptMessage],
|
||||
model_parameters: dict,
|
||||
tools: Optional[list[PromptMessageTool]] = None,
|
||||
stop: Optional[list[str]] = None,
|
||||
stop: Optional[Sequence[str]] = None,
|
||||
stream: bool = True,
|
||||
user: Optional[str] = None,
|
||||
) -> Union[LLMResult, Generator]:
|
||||
|
@ -601,7 +601,7 @@ if you are not sure about the structure.
|
|||
prompt_messages: list[PromptMessage],
|
||||
model_parameters: dict,
|
||||
tools: Optional[list[PromptMessageTool]] = None,
|
||||
stop: Optional[list[str]] = None,
|
||||
stop: Optional[Sequence[str]] = None,
|
||||
stream: bool = True,
|
||||
user: Optional[str] = None,
|
||||
callbacks: Optional[list[Callback]] = None,
|
||||
|
@ -647,7 +647,7 @@ if you are not sure about the structure.
|
|||
prompt_messages: list[PromptMessage],
|
||||
model_parameters: dict,
|
||||
tools: Optional[list[PromptMessageTool]] = None,
|
||||
stop: Optional[list[str]] = None,
|
||||
stop: Optional[Sequence[str]] = None,
|
||||
stream: bool = True,
|
||||
user: Optional[str] = None,
|
||||
callbacks: Optional[list[Callback]] = None,
|
||||
|
@ -694,7 +694,7 @@ if you are not sure about the structure.
|
|||
prompt_messages: list[PromptMessage],
|
||||
model_parameters: dict,
|
||||
tools: Optional[list[PromptMessageTool]] = None,
|
||||
stop: Optional[list[str]] = None,
|
||||
stop: Optional[Sequence[str]] = None,
|
||||
stream: bool = True,
|
||||
user: Optional[str] = None,
|
||||
callbacks: Optional[list[Callback]] = None,
|
||||
|
@ -742,7 +742,7 @@ if you are not sure about the structure.
|
|||
prompt_messages: list[PromptMessage],
|
||||
model_parameters: dict,
|
||||
tools: Optional[list[PromptMessageTool]] = None,
|
||||
stop: Optional[list[str]] = None,
|
||||
stop: Optional[Sequence[str]] = None,
|
||||
stream: bool = True,
|
||||
user: Optional[str] = None,
|
||||
callbacks: Optional[list[Callback]] = None,
|
||||
|
|
|
@ -8,6 +8,7 @@ features:
|
|||
- agent-thought
|
||||
- stream-tool-call
|
||||
- vision
|
||||
- audio
|
||||
model_properties:
|
||||
mode: chat
|
||||
context_size: 128000
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from collections.abc import Sequence
|
||||
from typing import cast
|
||||
|
||||
from core.model_runtime.entities import (
|
||||
|
@ -14,7 +15,7 @@ from core.prompt.simple_prompt_transform import ModelMode
|
|||
|
||||
class PromptMessageUtil:
|
||||
@staticmethod
|
||||
def prompt_messages_to_prompt_for_saving(model_mode: str, prompt_messages: list[PromptMessage]) -> list[dict]:
|
||||
def prompt_messages_to_prompt_for_saving(model_mode: str, prompt_messages: Sequence[PromptMessage]) -> list[dict]:
|
||||
"""
|
||||
Prompt messages to prompt for saving.
|
||||
:param model_mode: model mode
|
||||
|
|
|
@ -118,11 +118,11 @@ class FileSegment(Segment):
|
|||
|
||||
@property
|
||||
def log(self) -> str:
|
||||
return str(self.value)
|
||||
return ""
|
||||
|
||||
@property
|
||||
def text(self) -> str:
|
||||
return str(self.value)
|
||||
return ""
|
||||
|
||||
|
||||
class ArrayAnySegment(ArraySegment):
|
||||
|
@ -155,3 +155,11 @@ class ArrayFileSegment(ArraySegment):
|
|||
for item in self.value:
|
||||
items.append(item.markdown)
|
||||
return "\n".join(items)
|
||||
|
||||
@property
|
||||
def log(self) -> str:
|
||||
return ""
|
||||
|
||||
@property
|
||||
def text(self) -> str:
|
||||
return ""
|
||||
|
|
|
@ -39,7 +39,14 @@ class VisionConfig(BaseModel):
|
|||
|
||||
|
||||
class PromptConfig(BaseModel):
|
||||
jinja2_variables: Optional[list[VariableSelector]] = None
|
||||
jinja2_variables: Sequence[VariableSelector] = Field(default_factory=list)
|
||||
|
||||
@field_validator("jinja2_variables", mode="before")
|
||||
@classmethod
|
||||
def convert_none_jinja2_variables(cls, v: Any):
|
||||
if v is None:
|
||||
return []
|
||||
return v
|
||||
|
||||
|
||||
class LLMNodeChatModelMessage(ChatModelMessage):
|
||||
|
@ -53,7 +60,14 @@ class LLMNodeCompletionModelPromptTemplate(CompletionModelPromptTemplate):
|
|||
class LLMNodeData(BaseNodeData):
|
||||
model: ModelConfig
|
||||
prompt_template: Sequence[LLMNodeChatModelMessage] | LLMNodeCompletionModelPromptTemplate
|
||||
prompt_config: Optional[PromptConfig] = None
|
||||
prompt_config: PromptConfig = Field(default_factory=PromptConfig)
|
||||
memory: Optional[MemoryConfig] = None
|
||||
context: ContextConfig
|
||||
vision: VisionConfig = Field(default_factory=VisionConfig)
|
||||
|
||||
@field_validator("prompt_config", mode="before")
|
||||
@classmethod
|
||||
def convert_none_prompt_config(cls, v: Any):
|
||||
if v is None:
|
||||
return PromptConfig()
|
||||
return v
|
||||
|
|
|
@ -24,3 +24,11 @@ class LLMModeRequiredError(LLMNodeError):
|
|||
|
||||
class NoPromptFoundError(LLMNodeError):
|
||||
"""Raised when no prompt is found in the LLM configuration."""
|
||||
|
||||
|
||||
class NotSupportedPromptTypeError(LLMNodeError):
|
||||
"""Raised when the prompt type is not supported."""
|
||||
|
||||
|
||||
class MemoryRolePrefixRequiredError(LLMNodeError):
|
||||
"""Raised when memory role prefix is required for completion model."""
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import json
|
||||
import logging
|
||||
from collections.abc import Generator, Mapping, Sequence
|
||||
from typing import TYPE_CHECKING, Any, Optional, cast
|
||||
|
||||
|
@ -6,21 +7,26 @@ from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEnti
|
|||
from core.entities.model_entities import ModelStatus
|
||||
from core.entities.provider_entities import QuotaUnit
|
||||
from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotInitError, QuotaExceededError
|
||||
from core.file import FileType, file_manager
|
||||
from core.helper.code_executor import CodeExecutor, CodeLanguage
|
||||
from core.memory.token_buffer_memory import TokenBufferMemory
|
||||
from core.model_manager import ModelInstance, ModelManager
|
||||
from core.model_runtime.entities import (
|
||||
AudioPromptMessageContent,
|
||||
ImagePromptMessageContent,
|
||||
PromptMessage,
|
||||
PromptMessageContentType,
|
||||
TextPromptMessageContent,
|
||||
VideoPromptMessageContent,
|
||||
)
|
||||
from core.model_runtime.entities.llm_entities import LLMResult, LLMUsage
|
||||
from core.model_runtime.entities.model_entities import ModelType
|
||||
from core.model_runtime.entities.message_entities import (
|
||||
AssistantPromptMessage,
|
||||
PromptMessageRole,
|
||||
SystemPromptMessage,
|
||||
UserPromptMessage,
|
||||
)
|
||||
from core.model_runtime.entities.model_entities import ModelFeature, ModelPropertyKey, ModelType
|
||||
from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel
|
||||
from core.model_runtime.utils.encoders import jsonable_encoder
|
||||
from core.prompt.advanced_prompt_transform import AdvancedPromptTransform
|
||||
from core.prompt.entities.advanced_prompt_entities import CompletionModelPromptTemplate, MemoryConfig
|
||||
from core.prompt.utils.prompt_message_util import PromptMessageUtil
|
||||
from core.variables import (
|
||||
|
@ -32,8 +38,9 @@ from core.variables import (
|
|||
ObjectSegment,
|
||||
StringSegment,
|
||||
)
|
||||
from core.workflow.constants import SYSTEM_VARIABLE_NODE_ID
|
||||
from core.workflow.entities.node_entities import NodeRunMetadataKey, NodeRunResult
|
||||
from core.workflow.entities.variable_entities import VariableSelector
|
||||
from core.workflow.entities.variable_pool import VariablePool
|
||||
from core.workflow.enums import SystemVariableKey
|
||||
from core.workflow.graph_engine.entities.event import InNodeEvent
|
||||
from core.workflow.nodes.base import BaseNode
|
||||
|
@ -62,14 +69,18 @@ from .exc import (
|
|||
InvalidVariableTypeError,
|
||||
LLMModeRequiredError,
|
||||
LLMNodeError,
|
||||
MemoryRolePrefixRequiredError,
|
||||
ModelNotExistError,
|
||||
NoPromptFoundError,
|
||||
NotSupportedPromptTypeError,
|
||||
VariableNotFoundError,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.file.models import File
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LLMNode(BaseNode[LLMNodeData]):
|
||||
_node_data_cls = LLMNodeData
|
||||
|
@ -123,17 +134,13 @@ class LLMNode(BaseNode[LLMNodeData]):
|
|||
|
||||
# fetch prompt messages
|
||||
if self.node_data.memory:
|
||||
query = self.graph_runtime_state.variable_pool.get((SYSTEM_VARIABLE_NODE_ID, SystemVariableKey.QUERY))
|
||||
if not query:
|
||||
raise VariableNotFoundError("Query not found")
|
||||
query = query.text
|
||||
query = self.node_data.memory.query_prompt_template
|
||||
else:
|
||||
query = None
|
||||
|
||||
prompt_messages, stop = self._fetch_prompt_messages(
|
||||
system_query=query,
|
||||
inputs=inputs,
|
||||
files=files,
|
||||
user_query=query,
|
||||
user_files=files,
|
||||
context=context,
|
||||
memory=memory,
|
||||
model_config=model_config,
|
||||
|
@ -141,6 +148,8 @@ class LLMNode(BaseNode[LLMNodeData]):
|
|||
memory_config=self.node_data.memory,
|
||||
vision_enabled=self.node_data.vision.enabled,
|
||||
vision_detail=self.node_data.vision.configs.detail,
|
||||
variable_pool=self.graph_runtime_state.variable_pool,
|
||||
jinja2_variables=self.node_data.prompt_config.jinja2_variables,
|
||||
)
|
||||
|
||||
process_data = {
|
||||
|
@ -181,6 +190,17 @@ class LLMNode(BaseNode[LLMNodeData]):
|
|||
)
|
||||
)
|
||||
return
|
||||
except Exception as e:
|
||||
logger.exception(f"Node {self.node_id} failed to run: {e}")
|
||||
yield RunCompletedEvent(
|
||||
run_result=NodeRunResult(
|
||||
status=WorkflowNodeExecutionStatus.FAILED,
|
||||
error=str(e),
|
||||
inputs=node_inputs,
|
||||
process_data=process_data,
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
outputs = {"text": result_text, "usage": jsonable_encoder(usage), "finish_reason": finish_reason}
|
||||
|
||||
|
@ -203,8 +223,8 @@ class LLMNode(BaseNode[LLMNodeData]):
|
|||
self,
|
||||
node_data_model: ModelConfig,
|
||||
model_instance: ModelInstance,
|
||||
prompt_messages: list[PromptMessage],
|
||||
stop: Optional[list[str]] = None,
|
||||
prompt_messages: Sequence[PromptMessage],
|
||||
stop: Optional[Sequence[str]] = None,
|
||||
) -> Generator[NodeEvent, None, None]:
|
||||
db.session.close()
|
||||
|
||||
|
@ -519,9 +539,8 @@ class LLMNode(BaseNode[LLMNodeData]):
|
|||
def _fetch_prompt_messages(
|
||||
self,
|
||||
*,
|
||||
system_query: str | None = None,
|
||||
inputs: dict[str, str] | None = None,
|
||||
files: Sequence["File"],
|
||||
user_query: str | None = None,
|
||||
user_files: Sequence["File"],
|
||||
context: str | None = None,
|
||||
memory: TokenBufferMemory | None = None,
|
||||
model_config: ModelConfigWithCredentialsEntity,
|
||||
|
@ -529,58 +548,146 @@ class LLMNode(BaseNode[LLMNodeData]):
|
|||
memory_config: MemoryConfig | None = None,
|
||||
vision_enabled: bool = False,
|
||||
vision_detail: ImagePromptMessageContent.DETAIL,
|
||||
) -> tuple[list[PromptMessage], Optional[list[str]]]:
|
||||
inputs = inputs or {}
|
||||
variable_pool: VariablePool,
|
||||
jinja2_variables: Sequence[VariableSelector],
|
||||
) -> tuple[Sequence[PromptMessage], Optional[Sequence[str]]]:
|
||||
prompt_messages = []
|
||||
|
||||
prompt_transform = AdvancedPromptTransform(with_variable_tmpl=True)
|
||||
prompt_messages = prompt_transform.get_prompt(
|
||||
prompt_template=prompt_template,
|
||||
inputs=inputs,
|
||||
query=system_query or "",
|
||||
files=files,
|
||||
if isinstance(prompt_template, list):
|
||||
# For chat model
|
||||
prompt_messages.extend(
|
||||
_handle_list_messages(
|
||||
messages=prompt_template,
|
||||
context=context,
|
||||
memory_config=memory_config,
|
||||
jinja2_variables=jinja2_variables,
|
||||
variable_pool=variable_pool,
|
||||
vision_detail_config=vision_detail,
|
||||
)
|
||||
)
|
||||
|
||||
# Get memory messages for chat mode
|
||||
memory_messages = _handle_memory_chat_mode(
|
||||
memory=memory,
|
||||
memory_config=memory_config,
|
||||
model_config=model_config,
|
||||
)
|
||||
stop = model_config.stop
|
||||
# Extend prompt_messages with memory messages
|
||||
prompt_messages.extend(memory_messages)
|
||||
|
||||
# Add current query to the prompt messages
|
||||
if user_query:
|
||||
message = LLMNodeChatModelMessage(
|
||||
text=user_query,
|
||||
role=PromptMessageRole.USER,
|
||||
edition_type="basic",
|
||||
)
|
||||
prompt_messages.extend(
|
||||
_handle_list_messages(
|
||||
messages=[message],
|
||||
context="",
|
||||
jinja2_variables=[],
|
||||
variable_pool=variable_pool,
|
||||
vision_detail_config=vision_detail,
|
||||
)
|
||||
)
|
||||
|
||||
elif isinstance(prompt_template, LLMNodeCompletionModelPromptTemplate):
|
||||
# For completion model
|
||||
prompt_messages.extend(
|
||||
_handle_completion_template(
|
||||
template=prompt_template,
|
||||
context=context,
|
||||
jinja2_variables=jinja2_variables,
|
||||
variable_pool=variable_pool,
|
||||
)
|
||||
)
|
||||
|
||||
# Get memory text for completion model
|
||||
memory_text = _handle_memory_completion_mode(
|
||||
memory=memory,
|
||||
memory_config=memory_config,
|
||||
model_config=model_config,
|
||||
)
|
||||
# Insert histories into the prompt
|
||||
prompt_content = prompt_messages[0].content
|
||||
if "#histories#" in prompt_content:
|
||||
prompt_content = prompt_content.replace("#histories#", memory_text)
|
||||
else:
|
||||
prompt_content = memory_text + "\n" + prompt_content
|
||||
prompt_messages[0].content = prompt_content
|
||||
|
||||
# Add current query to the prompt message
|
||||
if user_query:
|
||||
prompt_content = prompt_messages[0].content.replace("#sys.query#", user_query)
|
||||
prompt_messages[0].content = prompt_content
|
||||
else:
|
||||
errmsg = f"Prompt type {type(prompt_template)} is not supported"
|
||||
logger.warning(errmsg)
|
||||
raise NotSupportedPromptTypeError(errmsg)
|
||||
|
||||
if vision_enabled and user_files:
|
||||
file_prompts = []
|
||||
for file in user_files:
|
||||
file_prompt = file_manager.to_prompt_message_content(file, image_detail_config=vision_detail)
|
||||
file_prompts.append(file_prompt)
|
||||
if (
|
||||
len(prompt_messages) > 0
|
||||
and isinstance(prompt_messages[-1], UserPromptMessage)
|
||||
and isinstance(prompt_messages[-1].content, list)
|
||||
):
|
||||
prompt_messages[-1] = UserPromptMessage(content=prompt_messages[-1].content + file_prompts)
|
||||
else:
|
||||
prompt_messages.append(UserPromptMessage(content=file_prompts))
|
||||
|
||||
# Filter prompt messages
|
||||
filtered_prompt_messages = []
|
||||
for prompt_message in prompt_messages:
|
||||
if isinstance(prompt_message.content, list):
|
||||
prompt_message_content = []
|
||||
for content_item in prompt_message.content:
|
||||
# Skip content if features are not defined
|
||||
if not model_config.model_schema.features:
|
||||
if content_item.type != PromptMessageContentType.TEXT:
|
||||
continue
|
||||
prompt_message_content.append(content_item)
|
||||
continue
|
||||
|
||||
# Skip content if corresponding feature is not supported
|
||||
if (
|
||||
(
|
||||
content_item.type == PromptMessageContentType.IMAGE
|
||||
and ModelFeature.VISION not in model_config.model_schema.features
|
||||
)
|
||||
or (
|
||||
content_item.type == PromptMessageContentType.DOCUMENT
|
||||
and ModelFeature.DOCUMENT not in model_config.model_schema.features
|
||||
)
|
||||
or (
|
||||
content_item.type == PromptMessageContentType.VIDEO
|
||||
and ModelFeature.VIDEO not in model_config.model_schema.features
|
||||
)
|
||||
or (
|
||||
content_item.type == PromptMessageContentType.AUDIO
|
||||
and ModelFeature.AUDIO not in model_config.model_schema.features
|
||||
)
|
||||
):
|
||||
continue
|
||||
prompt_message_content.append(content_item)
|
||||
if len(prompt_message_content) == 1 and prompt_message_content[0].type == PromptMessageContentType.TEXT:
|
||||
prompt_message.content = prompt_message_content[0].data
|
||||
else:
|
||||
prompt_message.content = prompt_message_content
|
||||
if prompt_message.is_empty():
|
||||
continue
|
||||
|
||||
if not isinstance(prompt_message.content, str):
|
||||
prompt_message_content = []
|
||||
for content_item in prompt_message.content or []:
|
||||
# Skip image if vision is disabled
|
||||
if not vision_enabled and content_item.type == PromptMessageContentType.IMAGE:
|
||||
continue
|
||||
|
||||
if isinstance(content_item, ImagePromptMessageContent):
|
||||
# Override vision config if LLM node has vision config,
|
||||
# cuz vision detail is related to the configuration from FileUpload feature.
|
||||
content_item.detail = vision_detail
|
||||
prompt_message_content.append(content_item)
|
||||
elif isinstance(
|
||||
content_item, TextPromptMessageContent | AudioPromptMessageContent | VideoPromptMessageContent
|
||||
):
|
||||
prompt_message_content.append(content_item)
|
||||
|
||||
if len(prompt_message_content) > 1:
|
||||
prompt_message.content = prompt_message_content
|
||||
elif (
|
||||
len(prompt_message_content) == 1 and prompt_message_content[0].type == PromptMessageContentType.TEXT
|
||||
):
|
||||
prompt_message.content = prompt_message_content[0].data
|
||||
|
||||
filtered_prompt_messages.append(prompt_message)
|
||||
|
||||
if not filtered_prompt_messages:
|
||||
if len(filtered_prompt_messages) == 0:
|
||||
raise NoPromptFoundError(
|
||||
"No prompt found in the LLM configuration. "
|
||||
"Please ensure a prompt is properly configured before proceeding."
|
||||
)
|
||||
|
||||
stop = model_config.stop
|
||||
return filtered_prompt_messages, stop
|
||||
|
||||
@classmethod
|
||||
|
@ -715,3 +822,198 @@ class LLMNode(BaseNode[LLMNodeData]):
|
|||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _combine_text_message_with_role(*, text: str, role: PromptMessageRole):
|
||||
match role:
|
||||
case PromptMessageRole.USER:
|
||||
return UserPromptMessage(content=[TextPromptMessageContent(data=text)])
|
||||
case PromptMessageRole.ASSISTANT:
|
||||
return AssistantPromptMessage(content=[TextPromptMessageContent(data=text)])
|
||||
case PromptMessageRole.SYSTEM:
|
||||
return SystemPromptMessage(content=[TextPromptMessageContent(data=text)])
|
||||
raise NotImplementedError(f"Role {role} is not supported")
|
||||
|
||||
|
||||
def _render_jinja2_message(
|
||||
*,
|
||||
template: str,
|
||||
jinjia2_variables: Sequence[VariableSelector],
|
||||
variable_pool: VariablePool,
|
||||
):
|
||||
if not template:
|
||||
return ""
|
||||
|
||||
jinjia2_inputs = {}
|
||||
for jinja2_variable in jinjia2_variables:
|
||||
variable = variable_pool.get(jinja2_variable.value_selector)
|
||||
jinjia2_inputs[jinja2_variable.variable] = variable.to_object() if variable else ""
|
||||
code_execute_resp = CodeExecutor.execute_workflow_code_template(
|
||||
language=CodeLanguage.JINJA2,
|
||||
code=template,
|
||||
inputs=jinjia2_inputs,
|
||||
)
|
||||
result_text = code_execute_resp["result"]
|
||||
return result_text
|
||||
|
||||
|
||||
def _handle_list_messages(
|
||||
*,
|
||||
messages: Sequence[LLMNodeChatModelMessage],
|
||||
context: Optional[str],
|
||||
jinja2_variables: Sequence[VariableSelector],
|
||||
variable_pool: VariablePool,
|
||||
vision_detail_config: ImagePromptMessageContent.DETAIL,
|
||||
) -> Sequence[PromptMessage]:
|
||||
prompt_messages = []
|
||||
for message in messages:
|
||||
if message.edition_type == "jinja2":
|
||||
result_text = _render_jinja2_message(
|
||||
template=message.jinja2_text or "",
|
||||
jinjia2_variables=jinja2_variables,
|
||||
variable_pool=variable_pool,
|
||||
)
|
||||
prompt_message = _combine_text_message_with_role(text=result_text, role=message.role)
|
||||
prompt_messages.append(prompt_message)
|
||||
else:
|
||||
# Get segment group from basic message
|
||||
if context:
|
||||
template = message.text.replace("{#context#}", context)
|
||||
else:
|
||||
template = message.text
|
||||
segment_group = variable_pool.convert_template(template)
|
||||
|
||||
# Process segments for images
|
||||
file_contents = []
|
||||
for segment in segment_group.value:
|
||||
if isinstance(segment, ArrayFileSegment):
|
||||
for file in segment.value:
|
||||
if file.type in {FileType.IMAGE, FileType.VIDEO, FileType.AUDIO}:
|
||||
file_content = file_manager.to_prompt_message_content(
|
||||
file, image_detail_config=vision_detail_config
|
||||
)
|
||||
file_contents.append(file_content)
|
||||
if isinstance(segment, FileSegment):
|
||||
file = segment.value
|
||||
if file.type in {FileType.IMAGE, FileType.VIDEO, FileType.AUDIO}:
|
||||
file_content = file_manager.to_prompt_message_content(
|
||||
file, image_detail_config=vision_detail_config
|
||||
)
|
||||
file_contents.append(file_content)
|
||||
|
||||
# Create message with text from all segments
|
||||
plain_text = segment_group.text
|
||||
if plain_text:
|
||||
prompt_message = _combine_text_message_with_role(text=plain_text, role=message.role)
|
||||
prompt_messages.append(prompt_message)
|
||||
|
||||
if file_contents:
|
||||
# Create message with image contents
|
||||
prompt_message = UserPromptMessage(content=file_contents)
|
||||
prompt_messages.append(prompt_message)
|
||||
|
||||
return prompt_messages
|
||||
|
||||
|
||||
def _calculate_rest_token(
|
||||
*, prompt_messages: list[PromptMessage], model_config: ModelConfigWithCredentialsEntity
|
||||
) -> int:
|
||||
rest_tokens = 2000
|
||||
|
||||
model_context_tokens = model_config.model_schema.model_properties.get(ModelPropertyKey.CONTEXT_SIZE)
|
||||
if model_context_tokens:
|
||||
model_instance = ModelInstance(
|
||||
provider_model_bundle=model_config.provider_model_bundle, model=model_config.model
|
||||
)
|
||||
|
||||
curr_message_tokens = model_instance.get_llm_num_tokens(prompt_messages)
|
||||
|
||||
max_tokens = 0
|
||||
for parameter_rule in model_config.model_schema.parameter_rules:
|
||||
if parameter_rule.name == "max_tokens" or (
|
||||
parameter_rule.use_template and parameter_rule.use_template == "max_tokens"
|
||||
):
|
||||
max_tokens = (
|
||||
model_config.parameters.get(parameter_rule.name)
|
||||
or model_config.parameters.get(str(parameter_rule.use_template))
|
||||
or 0
|
||||
)
|
||||
|
||||
rest_tokens = model_context_tokens - max_tokens - curr_message_tokens
|
||||
rest_tokens = max(rest_tokens, 0)
|
||||
|
||||
return rest_tokens
|
||||
|
||||
|
||||
def _handle_memory_chat_mode(
|
||||
*,
|
||||
memory: TokenBufferMemory | None,
|
||||
memory_config: MemoryConfig | None,
|
||||
model_config: ModelConfigWithCredentialsEntity,
|
||||
) -> Sequence[PromptMessage]:
|
||||
memory_messages = []
|
||||
# Get messages from memory for chat model
|
||||
if memory and memory_config:
|
||||
rest_tokens = _calculate_rest_token(prompt_messages=[], model_config=model_config)
|
||||
memory_messages = memory.get_history_prompt_messages(
|
||||
max_token_limit=rest_tokens,
|
||||
message_limit=memory_config.window.size if memory_config.window.enabled else None,
|
||||
)
|
||||
return memory_messages
|
||||
|
||||
|
||||
def _handle_memory_completion_mode(
|
||||
*,
|
||||
memory: TokenBufferMemory | None,
|
||||
memory_config: MemoryConfig | None,
|
||||
model_config: ModelConfigWithCredentialsEntity,
|
||||
) -> str:
|
||||
memory_text = ""
|
||||
# Get history text from memory for completion model
|
||||
if memory and memory_config:
|
||||
rest_tokens = _calculate_rest_token(prompt_messages=[], model_config=model_config)
|
||||
if not memory_config.role_prefix:
|
||||
raise MemoryRolePrefixRequiredError("Memory role prefix is required for completion model.")
|
||||
memory_text = memory.get_history_prompt_text(
|
||||
max_token_limit=rest_tokens,
|
||||
message_limit=memory_config.window.size if memory_config.window.enabled else None,
|
||||
human_prefix=memory_config.role_prefix.user,
|
||||
ai_prefix=memory_config.role_prefix.assistant,
|
||||
)
|
||||
return memory_text
|
||||
|
||||
|
||||
def _handle_completion_template(
|
||||
*,
|
||||
template: LLMNodeCompletionModelPromptTemplate,
|
||||
context: Optional[str],
|
||||
jinja2_variables: Sequence[VariableSelector],
|
||||
variable_pool: VariablePool,
|
||||
) -> Sequence[PromptMessage]:
|
||||
"""Handle completion template processing outside of LLMNode class.
|
||||
|
||||
Args:
|
||||
template: The completion model prompt template
|
||||
context: Optional context string
|
||||
jinja2_variables: Variables for jinja2 template rendering
|
||||
variable_pool: Variable pool for template conversion
|
||||
|
||||
Returns:
|
||||
Sequence of prompt messages
|
||||
"""
|
||||
prompt_messages = []
|
||||
if template.edition_type == "jinja2":
|
||||
result_text = _render_jinja2_message(
|
||||
template=template.jinja2_text or "",
|
||||
jinjia2_variables=jinja2_variables,
|
||||
variable_pool=variable_pool,
|
||||
)
|
||||
else:
|
||||
if context:
|
||||
template_text = template.text.replace("{#context#}", context)
|
||||
else:
|
||||
template_text = template.text
|
||||
result_text = variable_pool.convert_template(template_text).text
|
||||
prompt_message = _combine_text_message_with_role(text=result_text, role=PromptMessageRole.USER)
|
||||
prompt_messages.append(prompt_message)
|
||||
return prompt_messages
|
||||
|
|
|
@ -86,12 +86,14 @@ class QuestionClassifierNode(LLMNode):
|
|||
)
|
||||
prompt_messages, stop = self._fetch_prompt_messages(
|
||||
prompt_template=prompt_template,
|
||||
system_query=query,
|
||||
user_query=query,
|
||||
memory=memory,
|
||||
model_config=model_config,
|
||||
files=files,
|
||||
user_files=files,
|
||||
vision_enabled=node_data.vision.enabled,
|
||||
vision_detail=node_data.vision.configs.detail,
|
||||
variable_pool=variable_pool,
|
||||
jinja2_variables=[],
|
||||
)
|
||||
|
||||
# handle invoke result
|
||||
|
|
61
api/poetry.lock
generated
61
api/poetry.lock
generated
|
@ -1,4 +1,4 @@
|
|||
# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "aiohappyeyeballs"
|
||||
|
@ -932,10 +932,6 @@ files = [
|
|||
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"},
|
||||
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"},
|
||||
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"},
|
||||
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"},
|
||||
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"},
|
||||
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"},
|
||||
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"},
|
||||
{file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"},
|
||||
{file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"},
|
||||
{file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"},
|
||||
|
@ -948,14 +944,8 @@ files = [
|
|||
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"},
|
||||
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"},
|
||||
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"},
|
||||
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"},
|
||||
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"},
|
||||
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"},
|
||||
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"},
|
||||
{file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"},
|
||||
{file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"},
|
||||
|
@ -966,24 +956,8 @@ files = [
|
|||
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"},
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"},
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"},
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"},
|
||||
|
@ -993,10 +967,6 @@ files = [
|
|||
{file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"},
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"},
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"},
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:aea440a510e14e818e67bfc4027880e2fb500c2ccb20ab21c7a7c8b5b4703d75"},
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:6974f52a02321b36847cd19d1b8e381bf39939c21efd6ee2fc13a28b0d99348c"},
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:a7e53012d2853a07a4a79c00643832161a910674a893d296c9f1259859a289d2"},
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:d7702622a8b40c49bffb46e1e3ba2e81268d5c04a34f460978c6b5517a34dd52"},
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"},
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"},
|
||||
{file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"},
|
||||
|
@ -1008,10 +978,6 @@ files = [
|
|||
{file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"},
|
||||
{file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"},
|
||||
{file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"},
|
||||
{file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:cb1dac1770878ade83f2ccdf7d25e494f05c9165f5246b46a621cc849341dc01"},
|
||||
{file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:3ee8a80d67a4334482d9712b8e83ca6b1d9bc7e351931252ebef5d8f7335a547"},
|
||||
{file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5e55da2c8724191e5b557f8e18943b1b4839b8efc3ef60d65985bcf6f587dd38"},
|
||||
{file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:d342778ef319e1026af243ed0a07c97acf3bad33b9f29e7ae6a1f68fd083e90c"},
|
||||
{file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"},
|
||||
{file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"},
|
||||
{file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"},
|
||||
|
@ -1024,10 +990,6 @@ files = [
|
|||
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"},
|
||||
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"},
|
||||
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"},
|
||||
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d2b35ca2c7f81d173d2fadc2f4f31e88cc5f7a39ae5b6db5513cf3383b0e0ec7"},
|
||||
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:af6fa6817889314555aede9a919612b23739395ce767fe7fcbea9a80bf140fe5"},
|
||||
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2feb1d960f760a575dbc5ab3b1c00504b24caaf6986e2dc2b01c09c87866a943"},
|
||||
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4410f84b33374409552ac9b6903507cdb31cd30d2501fc5ca13d18f73548444a"},
|
||||
{file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"},
|
||||
{file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"},
|
||||
{file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"},
|
||||
|
@ -1040,10 +1002,6 @@ files = [
|
|||
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"},
|
||||
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"},
|
||||
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"},
|
||||
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"},
|
||||
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"},
|
||||
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"},
|
||||
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"},
|
||||
{file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"},
|
||||
{file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"},
|
||||
{file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"},
|
||||
|
@ -2453,6 +2411,21 @@ files = [
|
|||
[package.extras]
|
||||
test = ["pytest (>=6)"]
|
||||
|
||||
[[package]]
|
||||
name = "faker"
|
||||
version = "32.1.0"
|
||||
description = "Faker is a Python package that generates fake data for you."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "Faker-32.1.0-py3-none-any.whl", hash = "sha256:c77522577863c264bdc9dad3a2a750ad3f7ee43ff8185072e482992288898814"},
|
||||
{file = "faker-32.1.0.tar.gz", hash = "sha256:aac536ba04e6b7beb2332c67df78485fc29c1880ff723beac6d1efd45e2f10f5"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
python-dateutil = ">=2.4"
|
||||
typing-extensions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "fal-client"
|
||||
version = "0.5.6"
|
||||
|
@ -11078,4 +11051,4 @@ cffi = ["cffi (>=1.11)"]
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.10,<3.13"
|
||||
content-hash = "2ba4b464eebc26598f290fa94713acc44c588f902176e6efa80622911d40f0ac"
|
||||
content-hash = "cf4e0467f622e58b51411ee1d784928962f52dbf877b8ee013c810909a1f07db"
|
||||
|
|
|
@ -267,6 +267,7 @@ weaviate-client = "~3.21.0"
|
|||
optional = true
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
coverage = "~7.2.4"
|
||||
faker = "~32.1.0"
|
||||
pytest = "~8.3.2"
|
||||
pytest-benchmark = "~4.0.0"
|
||||
pytest-env = "~1.1.3"
|
||||
|
|
|
@ -11,7 +11,6 @@ from core.model_runtime.entities.message_entities import (
|
|||
)
|
||||
from core.model_runtime.errors.validate import CredentialsValidateFailedError
|
||||
from core.model_runtime.model_providers.azure_ai_studio.llm.llm import AzureAIStudioLargeLanguageModel
|
||||
from tests.integration_tests.model_runtime.__mock.azure_ai_studio import setup_azure_ai_studio_mock
|
||||
|
||||
|
||||
@pytest.mark.parametrize("setup_azure_ai_studio_mock", [["chat"]], indirect=True)
|
||||
|
|
|
@ -4,29 +4,21 @@ import pytest
|
|||
|
||||
from core.model_runtime.entities.rerank_entities import RerankResult
|
||||
from core.model_runtime.errors.validate import CredentialsValidateFailedError
|
||||
from core.model_runtime.model_providers.azure_ai_studio.rerank.rerank import AzureAIStudioRerankModel
|
||||
from core.model_runtime.model_providers.azure_ai_studio.rerank.rerank import AzureRerankModel
|
||||
|
||||
|
||||
def test_validate_credentials():
|
||||
model = AzureAIStudioRerankModel()
|
||||
model = AzureRerankModel()
|
||||
|
||||
with pytest.raises(CredentialsValidateFailedError):
|
||||
model.validate_credentials(
|
||||
model="azure-ai-studio-rerank-v1",
|
||||
credentials={"api_key": "invalid_key", "api_base": os.getenv("AZURE_AI_STUDIO_API_BASE")},
|
||||
query="What is the capital of the United States?",
|
||||
docs=[
|
||||
"Carson City is the capital city of the American state of Nevada. At the 2010 United States "
|
||||
"Census, Carson City had a population of 55,274.",
|
||||
"The Commonwealth of the Northern Mariana Islands is a group of islands in the Pacific Ocean that "
|
||||
"are a political division controlled by the United States. Its capital is Saipan.",
|
||||
],
|
||||
score_threshold=0.8,
|
||||
)
|
||||
|
||||
|
||||
def test_invoke_model():
|
||||
model = AzureAIStudioRerankModel()
|
||||
model = AzureRerankModel()
|
||||
|
||||
result = model.invoke(
|
||||
model="azure-ai-studio-rerank-v1",
|
||||
|
|
|
@ -1,22 +1,61 @@
|
|||
from collections.abc import Sequence
|
||||
from typing import Optional
|
||||
|
||||
import pytest
|
||||
|
||||
from core.app.entities.app_invoke_entities import InvokeFrom
|
||||
from configs import dify_config
|
||||
from core.app.entities.app_invoke_entities import InvokeFrom, ModelConfigWithCredentialsEntity
|
||||
from core.entities.provider_configuration import ProviderConfiguration, ProviderModelBundle
|
||||
from core.entities.provider_entities import CustomConfiguration, SystemConfiguration
|
||||
from core.file import File, FileTransferMethod, FileType
|
||||
from core.model_runtime.entities.message_entities import ImagePromptMessageContent
|
||||
from core.model_runtime.entities.common_entities import I18nObject
|
||||
from core.model_runtime.entities.message_entities import (
|
||||
AssistantPromptMessage,
|
||||
ImagePromptMessageContent,
|
||||
PromptMessage,
|
||||
PromptMessageRole,
|
||||
SystemPromptMessage,
|
||||
TextPromptMessageContent,
|
||||
UserPromptMessage,
|
||||
)
|
||||
from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelFeature, ModelType, ProviderModel
|
||||
from core.model_runtime.entities.provider_entities import ConfigurateMethod, ProviderEntity
|
||||
from core.model_runtime.model_providers.model_provider_factory import ModelProviderFactory
|
||||
from core.prompt.entities.advanced_prompt_entities import MemoryConfig
|
||||
from core.variables import ArrayAnySegment, ArrayFileSegment, NoneSegment
|
||||
from core.workflow.entities.variable_pool import VariablePool
|
||||
from core.workflow.graph_engine import Graph, GraphInitParams, GraphRuntimeState
|
||||
from core.workflow.nodes.answer import AnswerStreamGenerateRoute
|
||||
from core.workflow.nodes.end import EndStreamParam
|
||||
from core.workflow.nodes.llm.entities import ContextConfig, LLMNodeData, ModelConfig, VisionConfig, VisionConfigOptions
|
||||
from core.workflow.nodes.llm.entities import (
|
||||
ContextConfig,
|
||||
LLMNodeChatModelMessage,
|
||||
LLMNodeData,
|
||||
ModelConfig,
|
||||
VisionConfig,
|
||||
VisionConfigOptions,
|
||||
)
|
||||
from core.workflow.nodes.llm.node import LLMNode
|
||||
from models.enums import UserFrom
|
||||
from models.provider import ProviderType
|
||||
from models.workflow import WorkflowType
|
||||
from tests.unit_tests.core.workflow.nodes.llm.test_scenarios import LLMNodeTestScenario
|
||||
|
||||
|
||||
class TestLLMNode:
|
||||
@pytest.fixture
|
||||
def llm_node(self):
|
||||
class MockTokenBufferMemory:
|
||||
def __init__(self, history_messages=None):
|
||||
self.history_messages = history_messages or []
|
||||
|
||||
def get_history_prompt_messages(
|
||||
self, max_token_limit: int = 2000, message_limit: Optional[int] = None
|
||||
) -> Sequence[PromptMessage]:
|
||||
if message_limit is not None:
|
||||
return self.history_messages[-message_limit * 2 :]
|
||||
return self.history_messages
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def llm_node():
|
||||
data = LLMNodeData(
|
||||
title="Test LLM",
|
||||
model=ModelConfig(provider="openai", name="gpt-3.5-turbo", mode="chat", completion_params={}),
|
||||
|
@ -70,7 +109,48 @@ class TestLLMNode:
|
|||
)
|
||||
return node
|
||||
|
||||
def test_fetch_files_with_file_segment(self, llm_node):
|
||||
|
||||
@pytest.fixture
|
||||
def model_config():
|
||||
# Create actual provider and model type instances
|
||||
model_provider_factory = ModelProviderFactory()
|
||||
provider_instance = model_provider_factory.get_provider_instance("openai")
|
||||
model_type_instance = provider_instance.get_model_instance(ModelType.LLM)
|
||||
|
||||
# Create a ProviderModelBundle
|
||||
provider_model_bundle = ProviderModelBundle(
|
||||
configuration=ProviderConfiguration(
|
||||
tenant_id="1",
|
||||
provider=provider_instance.get_provider_schema(),
|
||||
preferred_provider_type=ProviderType.CUSTOM,
|
||||
using_provider_type=ProviderType.CUSTOM,
|
||||
system_configuration=SystemConfiguration(enabled=False),
|
||||
custom_configuration=CustomConfiguration(provider=None),
|
||||
model_settings=[],
|
||||
),
|
||||
provider_instance=provider_instance,
|
||||
model_type_instance=model_type_instance,
|
||||
)
|
||||
|
||||
# Create and return a ModelConfigWithCredentialsEntity
|
||||
return ModelConfigWithCredentialsEntity(
|
||||
provider="openai",
|
||||
model="gpt-3.5-turbo",
|
||||
model_schema=AIModelEntity(
|
||||
model="gpt-3.5-turbo",
|
||||
label=I18nObject(en_US="GPT-3.5 Turbo"),
|
||||
model_type=ModelType.LLM,
|
||||
fetch_from=FetchFrom.CUSTOMIZABLE_MODEL,
|
||||
model_properties={},
|
||||
),
|
||||
mode="chat",
|
||||
credentials={},
|
||||
parameters={},
|
||||
provider_model_bundle=provider_model_bundle,
|
||||
)
|
||||
|
||||
|
||||
def test_fetch_files_with_file_segment(llm_node):
|
||||
file = File(
|
||||
id="1",
|
||||
tenant_id="test",
|
||||
|
@ -84,7 +164,8 @@ class TestLLMNode:
|
|||
result = llm_node._fetch_files(selector=["sys", "files"])
|
||||
assert result == [file]
|
||||
|
||||
def test_fetch_files_with_array_file_segment(self, llm_node):
|
||||
|
||||
def test_fetch_files_with_array_file_segment(llm_node):
|
||||
files = [
|
||||
File(
|
||||
id="1",
|
||||
|
@ -108,18 +189,296 @@ class TestLLMNode:
|
|||
result = llm_node._fetch_files(selector=["sys", "files"])
|
||||
assert result == files
|
||||
|
||||
def test_fetch_files_with_none_segment(self, llm_node):
|
||||
|
||||
def test_fetch_files_with_none_segment(llm_node):
|
||||
llm_node.graph_runtime_state.variable_pool.add(["sys", "files"], NoneSegment())
|
||||
|
||||
result = llm_node._fetch_files(selector=["sys", "files"])
|
||||
assert result == []
|
||||
|
||||
def test_fetch_files_with_array_any_segment(self, llm_node):
|
||||
|
||||
def test_fetch_files_with_array_any_segment(llm_node):
|
||||
llm_node.graph_runtime_state.variable_pool.add(["sys", "files"], ArrayAnySegment(value=[]))
|
||||
|
||||
result = llm_node._fetch_files(selector=["sys", "files"])
|
||||
assert result == []
|
||||
|
||||
def test_fetch_files_with_non_existent_variable(self, llm_node):
|
||||
|
||||
def test_fetch_files_with_non_existent_variable(llm_node):
|
||||
result = llm_node._fetch_files(selector=["sys", "files"])
|
||||
assert result == []
|
||||
|
||||
|
||||
def test_fetch_prompt_messages__vison_disabled(faker, llm_node, model_config):
|
||||
prompt_template = []
|
||||
llm_node.node_data.prompt_template = prompt_template
|
||||
|
||||
fake_vision_detail = faker.random_element(
|
||||
[ImagePromptMessageContent.DETAIL.HIGH, ImagePromptMessageContent.DETAIL.LOW]
|
||||
)
|
||||
fake_remote_url = faker.url()
|
||||
files = [
|
||||
File(
|
||||
id="1",
|
||||
tenant_id="test",
|
||||
type=FileType.IMAGE,
|
||||
filename="test1.jpg",
|
||||
transfer_method=FileTransferMethod.REMOTE_URL,
|
||||
remote_url=fake_remote_url,
|
||||
)
|
||||
]
|
||||
|
||||
fake_query = faker.sentence()
|
||||
|
||||
prompt_messages, _ = llm_node._fetch_prompt_messages(
|
||||
user_query=fake_query,
|
||||
user_files=files,
|
||||
context=None,
|
||||
memory=None,
|
||||
model_config=model_config,
|
||||
prompt_template=prompt_template,
|
||||
memory_config=None,
|
||||
vision_enabled=False,
|
||||
vision_detail=fake_vision_detail,
|
||||
variable_pool=llm_node.graph_runtime_state.variable_pool,
|
||||
jinja2_variables=[],
|
||||
)
|
||||
|
||||
assert prompt_messages == [UserPromptMessage(content=fake_query)]
|
||||
|
||||
|
||||
def test_fetch_prompt_messages__basic(faker, llm_node, model_config):
|
||||
# Setup dify config
|
||||
dify_config.MULTIMODAL_SEND_IMAGE_FORMAT = "url"
|
||||
dify_config.MULTIMODAL_SEND_VIDEO_FORMAT = "url"
|
||||
|
||||
# Generate fake values for prompt template
|
||||
fake_assistant_prompt = faker.sentence()
|
||||
fake_query = faker.sentence()
|
||||
fake_context = faker.sentence()
|
||||
fake_window_size = faker.random_int(min=1, max=3)
|
||||
fake_vision_detail = faker.random_element(
|
||||
[ImagePromptMessageContent.DETAIL.HIGH, ImagePromptMessageContent.DETAIL.LOW]
|
||||
)
|
||||
fake_remote_url = faker.url()
|
||||
|
||||
# Setup mock memory with history messages
|
||||
mock_history = [
|
||||
UserPromptMessage(content=faker.sentence()),
|
||||
AssistantPromptMessage(content=faker.sentence()),
|
||||
UserPromptMessage(content=faker.sentence()),
|
||||
AssistantPromptMessage(content=faker.sentence()),
|
||||
UserPromptMessage(content=faker.sentence()),
|
||||
AssistantPromptMessage(content=faker.sentence()),
|
||||
]
|
||||
|
||||
# Setup memory configuration
|
||||
memory_config = MemoryConfig(
|
||||
role_prefix=MemoryConfig.RolePrefix(user="Human", assistant="Assistant"),
|
||||
window=MemoryConfig.WindowConfig(enabled=True, size=fake_window_size),
|
||||
query_prompt_template=None,
|
||||
)
|
||||
|
||||
memory = MockTokenBufferMemory(history_messages=mock_history)
|
||||
|
||||
# Test scenarios covering different file input combinations
|
||||
test_scenarios = [
|
||||
LLMNodeTestScenario(
|
||||
description="No files",
|
||||
user_query=fake_query,
|
||||
user_files=[],
|
||||
features=[],
|
||||
vision_enabled=False,
|
||||
vision_detail=None,
|
||||
window_size=fake_window_size,
|
||||
prompt_template=[
|
||||
LLMNodeChatModelMessage(
|
||||
text=fake_context,
|
||||
role=PromptMessageRole.SYSTEM,
|
||||
edition_type="basic",
|
||||
),
|
||||
LLMNodeChatModelMessage(
|
||||
text="{#context#}",
|
||||
role=PromptMessageRole.USER,
|
||||
edition_type="basic",
|
||||
),
|
||||
LLMNodeChatModelMessage(
|
||||
text=fake_assistant_prompt,
|
||||
role=PromptMessageRole.ASSISTANT,
|
||||
edition_type="basic",
|
||||
),
|
||||
],
|
||||
expected_messages=[
|
||||
SystemPromptMessage(content=fake_context),
|
||||
UserPromptMessage(content=fake_context),
|
||||
AssistantPromptMessage(content=fake_assistant_prompt),
|
||||
]
|
||||
+ mock_history[fake_window_size * -2 :]
|
||||
+ [
|
||||
UserPromptMessage(content=fake_query),
|
||||
],
|
||||
),
|
||||
LLMNodeTestScenario(
|
||||
description="User files",
|
||||
user_query=fake_query,
|
||||
user_files=[
|
||||
File(
|
||||
tenant_id="test",
|
||||
type=FileType.IMAGE,
|
||||
filename="test1.jpg",
|
||||
transfer_method=FileTransferMethod.REMOTE_URL,
|
||||
remote_url=fake_remote_url,
|
||||
)
|
||||
],
|
||||
vision_enabled=True,
|
||||
vision_detail=fake_vision_detail,
|
||||
features=[ModelFeature.VISION],
|
||||
window_size=fake_window_size,
|
||||
prompt_template=[
|
||||
LLMNodeChatModelMessage(
|
||||
text=fake_context,
|
||||
role=PromptMessageRole.SYSTEM,
|
||||
edition_type="basic",
|
||||
),
|
||||
LLMNodeChatModelMessage(
|
||||
text="{#context#}",
|
||||
role=PromptMessageRole.USER,
|
||||
edition_type="basic",
|
||||
),
|
||||
LLMNodeChatModelMessage(
|
||||
text=fake_assistant_prompt,
|
||||
role=PromptMessageRole.ASSISTANT,
|
||||
edition_type="basic",
|
||||
),
|
||||
],
|
||||
expected_messages=[
|
||||
SystemPromptMessage(content=fake_context),
|
||||
UserPromptMessage(content=fake_context),
|
||||
AssistantPromptMessage(content=fake_assistant_prompt),
|
||||
]
|
||||
+ mock_history[fake_window_size * -2 :]
|
||||
+ [
|
||||
UserPromptMessage(
|
||||
content=[
|
||||
TextPromptMessageContent(data=fake_query),
|
||||
ImagePromptMessageContent(data=fake_remote_url, detail=fake_vision_detail),
|
||||
]
|
||||
),
|
||||
],
|
||||
),
|
||||
LLMNodeTestScenario(
|
||||
description="Prompt template with variable selector of File",
|
||||
user_query=fake_query,
|
||||
user_files=[],
|
||||
vision_enabled=False,
|
||||
vision_detail=fake_vision_detail,
|
||||
features=[ModelFeature.VISION],
|
||||
window_size=fake_window_size,
|
||||
prompt_template=[
|
||||
LLMNodeChatModelMessage(
|
||||
text="{{#input.image#}}",
|
||||
role=PromptMessageRole.USER,
|
||||
edition_type="basic",
|
||||
),
|
||||
],
|
||||
expected_messages=[
|
||||
UserPromptMessage(
|
||||
content=[
|
||||
ImagePromptMessageContent(data=fake_remote_url, detail=fake_vision_detail),
|
||||
]
|
||||
),
|
||||
]
|
||||
+ mock_history[fake_window_size * -2 :]
|
||||
+ [UserPromptMessage(content=fake_query)],
|
||||
file_variables={
|
||||
"input.image": File(
|
||||
tenant_id="test",
|
||||
type=FileType.IMAGE,
|
||||
filename="test1.jpg",
|
||||
transfer_method=FileTransferMethod.REMOTE_URL,
|
||||
remote_url=fake_remote_url,
|
||||
)
|
||||
},
|
||||
),
|
||||
LLMNodeTestScenario(
|
||||
description="Prompt template with variable selector of File without vision feature",
|
||||
user_query=fake_query,
|
||||
user_files=[],
|
||||
vision_enabled=True,
|
||||
vision_detail=fake_vision_detail,
|
||||
features=[],
|
||||
window_size=fake_window_size,
|
||||
prompt_template=[
|
||||
LLMNodeChatModelMessage(
|
||||
text="{{#input.image#}}",
|
||||
role=PromptMessageRole.USER,
|
||||
edition_type="basic",
|
||||
),
|
||||
],
|
||||
expected_messages=mock_history[fake_window_size * -2 :] + [UserPromptMessage(content=fake_query)],
|
||||
file_variables={
|
||||
"input.image": File(
|
||||
tenant_id="test",
|
||||
type=FileType.IMAGE,
|
||||
filename="test1.jpg",
|
||||
transfer_method=FileTransferMethod.REMOTE_URL,
|
||||
remote_url=fake_remote_url,
|
||||
)
|
||||
},
|
||||
),
|
||||
LLMNodeTestScenario(
|
||||
description="Prompt template with variable selector of File with video file and vision feature",
|
||||
user_query=fake_query,
|
||||
user_files=[],
|
||||
vision_enabled=True,
|
||||
vision_detail=fake_vision_detail,
|
||||
features=[ModelFeature.VISION],
|
||||
window_size=fake_window_size,
|
||||
prompt_template=[
|
||||
LLMNodeChatModelMessage(
|
||||
text="{{#input.image#}}",
|
||||
role=PromptMessageRole.USER,
|
||||
edition_type="basic",
|
||||
),
|
||||
],
|
||||
expected_messages=mock_history[fake_window_size * -2 :] + [UserPromptMessage(content=fake_query)],
|
||||
file_variables={
|
||||
"input.image": File(
|
||||
tenant_id="test",
|
||||
type=FileType.VIDEO,
|
||||
filename="test1.mp4",
|
||||
transfer_method=FileTransferMethod.REMOTE_URL,
|
||||
remote_url=fake_remote_url,
|
||||
extension="mp4",
|
||||
)
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
for scenario in test_scenarios:
|
||||
model_config.model_schema.features = scenario.features
|
||||
|
||||
for k, v in scenario.file_variables.items():
|
||||
selector = k.split(".")
|
||||
llm_node.graph_runtime_state.variable_pool.add(selector, v)
|
||||
|
||||
# Call the method under test
|
||||
prompt_messages, _ = llm_node._fetch_prompt_messages(
|
||||
user_query=scenario.user_query,
|
||||
user_files=scenario.user_files,
|
||||
context=fake_context,
|
||||
memory=memory,
|
||||
model_config=model_config,
|
||||
prompt_template=scenario.prompt_template,
|
||||
memory_config=memory_config,
|
||||
vision_enabled=scenario.vision_enabled,
|
||||
vision_detail=scenario.vision_detail,
|
||||
variable_pool=llm_node.graph_runtime_state.variable_pool,
|
||||
jinja2_variables=[],
|
||||
)
|
||||
|
||||
# Verify the result
|
||||
assert len(prompt_messages) == len(scenario.expected_messages), f"Scenario failed: {scenario.description}"
|
||||
assert (
|
||||
prompt_messages == scenario.expected_messages
|
||||
), f"Message content mismatch in scenario: {scenario.description}"
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
from collections.abc import Mapping, Sequence
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from core.file import File
|
||||
from core.model_runtime.entities.message_entities import PromptMessage
|
||||
from core.model_runtime.entities.model_entities import ModelFeature
|
||||
from core.workflow.nodes.llm.entities import LLMNodeChatModelMessage
|
||||
|
||||
|
||||
class LLMNodeTestScenario(BaseModel):
|
||||
"""Test scenario for LLM node testing."""
|
||||
|
||||
description: str = Field(..., description="Description of the test scenario")
|
||||
user_query: str = Field(..., description="User query input")
|
||||
user_files: Sequence[File] = Field(default_factory=list, description="List of user files")
|
||||
vision_enabled: bool = Field(default=False, description="Whether vision is enabled")
|
||||
vision_detail: str | None = Field(None, description="Vision detail level if vision is enabled")
|
||||
features: Sequence[ModelFeature] = Field(default_factory=list, description="List of model features")
|
||||
window_size: int = Field(..., description="Window size for memory")
|
||||
prompt_template: Sequence[LLMNodeChatModelMessage] = Field(..., description="Template for prompt messages")
|
||||
file_variables: Mapping[str, File | Sequence[File]] = Field(
|
||||
default_factory=dict, description="List of file variables"
|
||||
)
|
||||
expected_messages: Sequence[PromptMessage] = Field(..., description="Expected messages after processing")
|
|
@ -44,12 +44,6 @@ export const fileUpload: FileUpload = ({
|
|||
}
|
||||
|
||||
export const getFileExtension = (fileName: string, fileMimetype: string, isRemote?: boolean) => {
|
||||
if (fileMimetype)
|
||||
return mime.getExtension(fileMimetype) || ''
|
||||
|
||||
if (isRemote)
|
||||
return ''
|
||||
|
||||
if (fileName) {
|
||||
const fileNamePair = fileName.split('.')
|
||||
const fileNamePairLength = fileNamePair.length
|
||||
|
@ -58,6 +52,12 @@ export const getFileExtension = (fileName: string, fileMimetype: string, isRemot
|
|||
return fileNamePair[fileNamePairLength - 1]
|
||||
}
|
||||
|
||||
if (fileMimetype)
|
||||
return mime.getExtension(fileMimetype) || ''
|
||||
|
||||
if (isRemote)
|
||||
return ''
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
|
|
|
@ -144,6 +144,7 @@ const ConfigPromptItem: FC<Props> = ({
|
|||
onEditionTypeChange={onEditionTypeChange}
|
||||
varList={varList}
|
||||
handleAddVariable={handleAddVariable}
|
||||
isSupportFileVar
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
|||
handleStop,
|
||||
varInputs,
|
||||
runResult,
|
||||
filterJinjia2InputVar,
|
||||
} = useConfig(id, data)
|
||||
|
||||
const model = inputs.model
|
||||
|
@ -194,7 +195,7 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
|||
list={inputs.prompt_config?.jinja2_variables || []}
|
||||
onChange={handleVarListChange}
|
||||
onVarNameChange={handleVarNameChange}
|
||||
filterVar={filterVar}
|
||||
filterVar={filterJinjia2InputVar}
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
|
@ -233,6 +234,7 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
|||
hasSetBlockStatus={hasSetBlockStatus}
|
||||
nodesOutputVars={availableVars}
|
||||
availableNodes={availableNodesWithParent}
|
||||
isSupportFileVar
|
||||
/>
|
||||
|
||||
{inputs.memory.query_prompt_template && !inputs.memory.query_prompt_template.includes('{{#sys.query#}}') && (
|
||||
|
|
|
@ -278,11 +278,15 @@ const useConfig = (id: string, payload: LLMNodeType) => {
|
|||
}, [inputs, setInputs])
|
||||
|
||||
const filterInputVar = useCallback((varPayload: Var) => {
|
||||
return [VarType.number, VarType.string, VarType.secret, VarType.arrayString, VarType.arrayNumber, VarType.arrayFile].includes(varPayload.type)
|
||||
}, [])
|
||||
|
||||
const filterJinjia2InputVar = useCallback((varPayload: Var) => {
|
||||
return [VarType.number, VarType.string, VarType.secret, VarType.arrayString, VarType.arrayNumber].includes(varPayload.type)
|
||||
}, [])
|
||||
|
||||
const filterMemoryPromptVar = useCallback((varPayload: Var) => {
|
||||
return [VarType.arrayObject, VarType.array, VarType.number, VarType.string, VarType.secret, VarType.arrayString, VarType.arrayNumber].includes(varPayload.type)
|
||||
return [VarType.arrayObject, VarType.array, VarType.number, VarType.string, VarType.secret, VarType.arrayString, VarType.arrayNumber, VarType.arrayFile].includes(varPayload.type)
|
||||
}, [])
|
||||
|
||||
const {
|
||||
|
@ -406,6 +410,7 @@ const useConfig = (id: string, payload: LLMNodeType) => {
|
|||
handleRun,
|
||||
handleStop,
|
||||
runResult,
|
||||
filterJinjia2InputVar,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -591,6 +591,10 @@ const translation = {
|
|||
uploadFromComputerReadError: 'Lesen der Datei fehlgeschlagen, bitte versuchen Sie es erneut.',
|
||||
fileExtensionNotSupport: 'Dateiendung nicht bedient',
|
||||
},
|
||||
license: {
|
||||
expiring: 'Läuft an einem Tag ab',
|
||||
expiring_plural: 'Läuft in {{count}} Tagen ab',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -99,6 +99,12 @@ const translation = {
|
|||
noLoginMethodTip: 'Wenden Sie sich an den Systemadministrator, um eine Authentifizierungsmethode hinzuzufügen.',
|
||||
usePassword: 'Passwort verwenden',
|
||||
noLoginMethod: 'Authentifizierungsmethode nicht konfiguriert',
|
||||
licenseExpired: 'Lizenz abgelaufen',
|
||||
licenseLostTip: 'Fehler beim Verbinden des Dify-Lizenzservers. Wenden Sie sich an Ihren Administrator, um Dify weiterhin zu verwenden.',
|
||||
licenseInactive: 'Lizenz inaktiv',
|
||||
licenseInactiveTip: 'Die Dify Enterprise-Lizenz für Ihren Arbeitsbereich ist inaktiv. Wenden Sie sich an Ihren Administrator, um Dify weiterhin zu verwenden.',
|
||||
licenseExpiredTip: 'Die Dify Enterprise-Lizenz für Ihren Arbeitsbereich ist abgelaufen. Wenden Sie sich an Ihren Administrator, um Dify weiterhin zu verwenden.',
|
||||
licenseLost: 'Lizenz verloren',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -407,6 +407,7 @@ const translation = {
|
|||
},
|
||||
type: 'Art',
|
||||
binaryFileVariable: 'Variable der Binärdatei',
|
||||
extractListPlaceholder: 'Geben Sie den Index des Listeneintrags ein, geben Sie \'/\' ein, fügen Sie die Variable ein',
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Eingabevariablen',
|
||||
|
@ -618,6 +619,7 @@ const translation = {
|
|||
filterConditionKey: 'Bedingungsschlüssel filtern',
|
||||
filterCondition: 'Filter-Bedingung',
|
||||
selectVariableKeyPlaceholder: 'Untervariablenschlüssel auswählen',
|
||||
extractsCondition: 'Extrahieren des N-Elements',
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
|
|
|
@ -591,6 +591,10 @@ const translation = {
|
|||
pasteFileLinkInputPlaceholder: 'Introduzca la URL...',
|
||||
uploadFromComputerLimit: 'El archivo de carga no puede exceder {{size}}',
|
||||
},
|
||||
license: {
|
||||
expiring: 'Caduca en un día',
|
||||
expiring_plural: 'Caducando en {{count}} días',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -99,6 +99,12 @@ const translation = {
|
|||
noLoginMethod: 'Método de autenticación no configurado',
|
||||
setYourAccount: 'Configura tu cuenta',
|
||||
noLoginMethodTip: 'Póngase en contacto con el administrador del sistema para agregar un método de autenticación.',
|
||||
licenseInactive: 'Licencia inactiva',
|
||||
licenseInactiveTip: 'La licencia de Dify Enterprise para su espacio de trabajo está inactiva. Póngase en contacto con su administrador para seguir utilizando Dify.',
|
||||
licenseExpired: 'Licencia caducada',
|
||||
licenseLost: 'Licencia perdida',
|
||||
licenseExpiredTip: 'La licencia de Dify Enterprise para su espacio de trabajo ha caducado. Póngase en contacto con su administrador para seguir utilizando Dify.',
|
||||
licenseLostTip: 'No se pudo conectar el servidor de licencias de Dife. Póngase en contacto con su administrador para seguir utilizando Dify.',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -407,6 +407,7 @@ const translation = {
|
|||
},
|
||||
type: 'Tipo',
|
||||
binaryFileVariable: 'Variable de archivo binario',
|
||||
extractListPlaceholder: 'Introduzca el índice de elementos de la lista, escriba \'/\' insertar variable',
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Variables de entrada',
|
||||
|
@ -621,6 +622,7 @@ const translation = {
|
|||
filterConditionComparisonOperator: 'Operador de comparación de condiciones de filtro',
|
||||
asc: 'ASC',
|
||||
selectVariableKeyPlaceholder: 'Seleccione la clave de subvariable',
|
||||
extractsCondition: 'Extraiga el elemento N',
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
|
|
|
@ -591,6 +591,10 @@ const translation = {
|
|||
pasteFileLink: 'پیوند فایل را جایگذاری کنید',
|
||||
uploadFromComputerLimit: 'آپلود فایل نمی تواند از {{size}} تجاوز کند',
|
||||
},
|
||||
license: {
|
||||
expiring_plural: 'انقضا در {{count}} روز',
|
||||
expiring: 'انقضا در یک روز',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -99,6 +99,12 @@ const translation = {
|
|||
noLoginMethod: 'روش احراز هویت پیکربندی نشده است',
|
||||
noLoginMethodTip: 'لطفا برای افزودن روش احراز هویت با مدیر سیستم تماس بگیرید.',
|
||||
resetPasswordDesc: 'ایمیلی را که برای ثبت نام در Dify استفاده کرده اید تایپ کنید و ما یک ایمیل بازنشانی رمز عبور برای شما ارسال خواهیم کرد.',
|
||||
licenseInactive: 'مجوز غیر فعال',
|
||||
licenseLost: 'مجوز گم شده است',
|
||||
licenseExpired: 'مجوز منقضی شده است',
|
||||
licenseExpiredTip: 'مجوز Dify Enterprise برای فضای کاری شما منقضی شده است. لطفا برای ادامه استفاده از Dify با سرپرست خود تماس بگیرید.',
|
||||
licenseInactiveTip: 'مجوز Dify Enterprise برای فضای کاری شما غیرفعال است. لطفا برای ادامه استفاده از Dify با سرپرست خود تماس بگیرید.',
|
||||
licenseLostTip: 'اتصال سرور مجوز Dify انجام نشد. لطفا برای ادامه استفاده از Dify با سرپرست خود تماس بگیرید.',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -407,6 +407,7 @@ const translation = {
|
|||
},
|
||||
binaryFileVariable: 'متغیر فایل باینری',
|
||||
type: 'نوع',
|
||||
extractListPlaceholder: 'فهرست آیتم لیست را وارد کنید، متغیر درج \'/\' را تایپ کنید',
|
||||
},
|
||||
code: {
|
||||
inputVars: 'متغیرهای ورودی',
|
||||
|
@ -618,6 +619,7 @@ const translation = {
|
|||
filterConditionComparisonValue: 'مقدار شرایط فیلتر',
|
||||
selectVariableKeyPlaceholder: 'کلید متغیر فرعی را انتخاب کنید',
|
||||
asc: 'صعودی',
|
||||
extractsCondition: 'مورد N را استخراج کنید',
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
|
|
|
@ -591,6 +591,10 @@ const translation = {
|
|||
pasteFileLinkInvalid: 'Lien de fichier non valide',
|
||||
uploadFromComputerLimit: 'Le fichier de téléchargement ne peut pas dépasser {{size}}',
|
||||
},
|
||||
license: {
|
||||
expiring: 'Expirant dans un jour',
|
||||
expiring_plural: 'Expirant dans {{count}} jours',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -99,6 +99,12 @@ const translation = {
|
|||
noLoginMethodTip: 'Veuillez contacter l’administrateur système pour ajouter une méthode d’authentification.',
|
||||
resetPasswordDesc: 'Tapez l’adresse e-mail que vous avez utilisée pour vous inscrire sur Dify et nous vous enverrons un e-mail de réinitialisation de mot de passe.',
|
||||
usePassword: 'Utiliser le mot de passe',
|
||||
licenseInactiveTip: 'La licence Dify Enterprise de votre espace de travail est inactive. Veuillez contacter votre administrateur pour continuer à utiliser Dify.',
|
||||
licenseLostTip: 'Échec de la connexion au serveur de licences Dify. Veuillez contacter votre administrateur pour continuer à utiliser Dify.',
|
||||
licenseExpired: 'Licence expirée',
|
||||
licenseLost: 'Licence perdue',
|
||||
licenseExpiredTip: 'La licence Dify Enterprise de votre espace de travail a expiré. Veuillez contacter votre administrateur pour continuer à utiliser Dify.',
|
||||
licenseInactive: 'Licence inactive',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -407,6 +407,7 @@ const translation = {
|
|||
},
|
||||
binaryFileVariable: 'Variable de fichier binaire',
|
||||
type: 'Type',
|
||||
extractListPlaceholder: 'Entrez l’index de l’élément de liste, tapez \'/\' insérer la variable',
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Variables de saisie',
|
||||
|
@ -618,6 +619,7 @@ const translation = {
|
|||
limit: 'Haut N',
|
||||
orderBy: 'Trier par',
|
||||
filterConditionKey: 'Clé de condition de filtre',
|
||||
extractsCondition: 'Extraire l’élément N',
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
|
|
|
@ -613,6 +613,10 @@ const translation = {
|
|||
fileExtensionNotSupport: 'फ़ाइल एक्सटेंशन समर्थित नहीं है',
|
||||
uploadFromComputer: 'स्थानीय अपलोड',
|
||||
},
|
||||
license: {
|
||||
expiring: 'एक दिन में समाप्त हो रहा है',
|
||||
expiring_plural: '{{गिनती}} दिनों में समाप्त हो रहा है',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -104,6 +104,12 @@ const translation = {
|
|||
resetPasswordDesc: 'वह ईमेल टाइप करें जिसका उपयोग आपने Dify पर साइन अप करने के लिए किया था और हम आपको एक पासवर्ड रीसेट ईमेल भेजेंगे।',
|
||||
withSSO: 'एसएसओ के साथ जारी रखें',
|
||||
back: 'पीछे',
|
||||
licenseInactive: 'लाइसेंस निष्क्रिय',
|
||||
licenseExpired: 'लाइसेंस की समय सीमा समाप्त हो गई',
|
||||
licenseLost: 'लाइसेंस खो गया',
|
||||
licenseLostTip: 'Dify लायसेंस सर्वर से कनेक्ट करने में विफल. Dify का उपयोग जारी रखने के लिए कृपया अपने व्यवस्थापक से संपर्क करें.',
|
||||
licenseInactiveTip: 'आपके कार्यस्थल के लिए डिफाई एंटरप्राइज लाइसेंस निष्क्रिय है। कृपया डिफाई का उपयोग जारी रखने के लिए अपने प्रशासक से संपर्क करें।',
|
||||
licenseExpiredTip: 'आपके कार्यस्थल के लिए डिफाई एंटरप्राइज लाइसेंस समाप्त हो गया है। कृपया डिफाई का उपयोग जारी रखने के लिए अपने प्रशासक से संपर्क करें।',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -420,6 +420,7 @@ const translation = {
|
|||
},
|
||||
type: 'प्रकार',
|
||||
binaryFileVariable: 'बाइनरी फ़ाइल चर',
|
||||
extractListPlaceholder: 'सूची आइटम इंडेक्स दर्ज करें, \'/\' इन्सर्ट वेरिएबल टाइप करें',
|
||||
},
|
||||
code: {
|
||||
inputVars: 'इनपुट वेरिएबल्स',
|
||||
|
@ -638,6 +639,7 @@ const translation = {
|
|||
filterConditionComparisonOperator: 'फ़िल्टर शर्त तुलन ऑपरेटर',
|
||||
selectVariableKeyPlaceholder: 'उप चर कुंजी का चयन करें',
|
||||
inputVar: 'इनपुट वेरिएबल',
|
||||
extractsCondition: 'N आइटम निकालें',
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
|
|
|
@ -622,6 +622,10 @@ const translation = {
|
|||
pasteFileLink: 'Incolla il collegamento del file',
|
||||
uploadFromComputerReadError: 'Lettura del file non riuscita, riprovare.',
|
||||
},
|
||||
license: {
|
||||
expiring_plural: 'Scadenza tra {{count}} giorni',
|
||||
expiring: 'Scadenza in un giorno',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -109,6 +109,12 @@ const translation = {
|
|||
resetPasswordDesc: 'Digita l\'e-mail che hai utilizzato per registrarti su Dify e ti invieremo un\'e-mail per reimpostare la password.',
|
||||
noLoginMethodTip: 'Contatta l\'amministratore di sistema per aggiungere un metodo di autenticazione.',
|
||||
enterYourName: 'Inserisci il tuo nome utente',
|
||||
licenseLostTip: 'Impossibile connettersi al server licenze Dify. Contatta il tuo amministratore per continuare a utilizzare Dify.',
|
||||
licenseExpired: 'Licenza scaduta',
|
||||
licenseLost: 'Licenza persa',
|
||||
licenseExpiredTip: 'La licenza Dify Enterprise per la tua area di lavoro è scaduta. Contatta il tuo amministratore per continuare a utilizzare Dify.',
|
||||
licenseInactiveTip: 'La licenza Dify Enterprise per la tua area di lavoro è inattiva. Contatta il tuo amministratore per continuare a utilizzare Dify.',
|
||||
licenseInactive: 'Licenza inattiva',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -424,6 +424,7 @@ const translation = {
|
|||
},
|
||||
binaryFileVariable: 'Variabile file binario',
|
||||
type: 'Digitare',
|
||||
extractListPlaceholder: 'Inserisci l\'indice delle voci dell\'elenco, digita \'/\' inserisci la variabile',
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Variabili di Input',
|
||||
|
@ -645,6 +646,7 @@ const translation = {
|
|||
desc: 'DESC',
|
||||
filterConditionComparisonValue: 'Valore della condizione di filtro',
|
||||
orderBy: 'Ordina per',
|
||||
extractsCondition: 'Estrai l\'elemento N',
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
|
|
|
@ -591,6 +591,10 @@ const translation = {
|
|||
fileExtensionNotSupport: 'ファイル拡張子はサポートされていません',
|
||||
pasteFileLinkInvalid: '無効なファイルリンク',
|
||||
},
|
||||
license: {
|
||||
expiring_plural: '有効期限 {{count}} 日',
|
||||
expiring: '1日で有効期限が切れます',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -99,6 +99,12 @@ const translation = {
|
|||
sendVerificationCode: '確認コードの送信',
|
||||
enterYourName: 'ユーザー名を入力してください',
|
||||
resetPasswordDesc: 'Difyへのサインアップに使用したメールアドレスを入力すると、パスワードリセットメールが送信されます。',
|
||||
licenseLost: 'ライセンスを失った',
|
||||
licenseExpiredTip: 'ワークスペースの Dify Enterprise ライセンスの有効期限が切れています。Difyを引き続き使用するには、管理者に連絡してください。',
|
||||
licenseInactive: 'ライセンスが非アクティブです',
|
||||
licenseInactiveTip: 'ワークスペースの Dify Enterprise ライセンスが非アクティブです。Difyを引き続き使用するには、管理者に連絡してください。',
|
||||
licenseExpired: 'ライセンスの有効期限が切れています',
|
||||
licenseLostTip: 'Difyライセンスサーバーへの接続に失敗しました。続けてDifyを使用するために管理者に連絡してください。',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -407,6 +407,7 @@ const translation = {
|
|||
},
|
||||
type: 'タイプ',
|
||||
binaryFileVariable: 'バイナリファイル変数',
|
||||
extractListPlaceholder: 'リスト項目のインデックスを入力し、変数を挿入 \'/\' と入力します',
|
||||
},
|
||||
code: {
|
||||
inputVars: '入力変数',
|
||||
|
@ -619,6 +620,7 @@ const translation = {
|
|||
filterConditionComparisonOperator: 'フィルター条件を比較オペレーター',
|
||||
inputVar: '入力変数',
|
||||
desc: 'DESC',
|
||||
extractsCondition: 'N個のアイテムを抽出します',
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
|
|
|
@ -587,6 +587,10 @@ const translation = {
|
|||
uploadFromComputerLimit: '업로드 파일은 {{size}}를 초과할 수 없습니다.',
|
||||
uploadFromComputerUploadError: '파일 업로드에 실패했습니다. 다시 업로드하십시오.',
|
||||
},
|
||||
license: {
|
||||
expiring_plural: '{{count}}일 후에 만료',
|
||||
expiring: '하루 후에 만료',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -99,6 +99,12 @@ const translation = {
|
|||
enterYourName: '사용자 이름을 입력해 주세요',
|
||||
noLoginMethodTip: '인증 방법을 추가하려면 시스템 관리자에게 문의하십시오.',
|
||||
resetPasswordDesc: 'Dify에 가입할 때 사용한 이메일을 입력하면 비밀번호 재설정 이메일을 보내드립니다.',
|
||||
licenseInactiveTip: '작업 영역에 대한 Dify Enterprise 라이선스가 비활성 상태입니다. Dify를 계속 사용하려면 관리자에게 문의하십시오.',
|
||||
licenseLost: '라이센스 분실',
|
||||
licenseLostTip: 'Dify 라이선스 서버에 연결하지 못했습니다. Dify를 계속 사용하려면 관리자에게 문의하십시오.',
|
||||
licenseInactive: 'License Inactive(라이선스 비활성)',
|
||||
licenseExpired: '라이센스가 만료되었습니다.',
|
||||
licenseExpiredTip: '작업 영역에 대한 Dify Enterprise 라이선스가 만료되었습니다. Dify를 계속 사용하려면 관리자에게 문의하십시오.',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -407,6 +407,7 @@ const translation = {
|
|||
},
|
||||
type: '형',
|
||||
binaryFileVariable: '바이너리 파일 변수',
|
||||
extractListPlaceholder: '목록 항목 인덱스 입력, \'/\' 변수 삽입',
|
||||
},
|
||||
code: {
|
||||
inputVars: '입력 변수',
|
||||
|
@ -618,6 +619,7 @@ const translation = {
|
|||
orderBy: '정렬 기준',
|
||||
selectVariableKeyPlaceholder: '하위 변수 키 선택',
|
||||
filterConditionComparisonOperator: '필터 조건 비교 연산자',
|
||||
extractsCondition: 'N 항목을 추출합니다.',
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
|
|
|
@ -609,6 +609,10 @@ const translation = {
|
|||
fileExtensionNotSupport: 'Rozszerzenie pliku nie jest obsługiwane',
|
||||
uploadFromComputer: 'Przesyłanie lokalne',
|
||||
},
|
||||
license: {
|
||||
expiring_plural: 'Wygasa za {{count}} dni',
|
||||
expiring: 'Wygasa w ciągu jednego dnia',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -104,6 +104,12 @@ const translation = {
|
|||
or: 'LUB',
|
||||
noLoginMethodTip: 'Skontaktuj się z administratorem systemu, aby dodać metodę uwierzytelniania.',
|
||||
noLoginMethod: 'Nie skonfigurowano metody uwierzytelniania',
|
||||
licenseLost: 'Utrata licencji',
|
||||
licenseExpired: 'Licencja wygasła',
|
||||
licenseInactive: 'Licencja nieaktywna',
|
||||
licenseExpiredTip: 'Licencja Dify Enterprise dla Twojego obszaru roboczego wygasła. Skontaktuj się z administratorem, aby kontynuować korzystanie z Dify.',
|
||||
licenseLostTip: 'Nie udało się nawiązać połączenia z serwerem licencji Dify. Skontaktuj się z administratorem, aby kontynuować korzystanie z Dify.',
|
||||
licenseInactiveTip: 'Licencja Dify Enterprise dla Twojego obszaru roboczego jest nieaktywna. Skontaktuj się z administratorem, aby kontynuować korzystanie z Dify.',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -407,6 +407,7 @@ const translation = {
|
|||
},
|
||||
type: 'Typ',
|
||||
binaryFileVariable: 'Binarna zmienna pliku',
|
||||
extractListPlaceholder: 'Wprowadź indeks elementu listy, wpisz "/" wstaw zmienną',
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Zmienne wejściowe',
|
||||
|
@ -618,6 +619,7 @@ const translation = {
|
|||
filterCondition: 'Stan filtra',
|
||||
filterConditionComparisonValue: 'Wartość warunku filtru',
|
||||
selectVariableKeyPlaceholder: 'Wybierz klucz zmiennej podrzędnej',
|
||||
extractsCondition: 'Wyodrębnij element N',
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
|
|
|
@ -591,6 +591,10 @@ const translation = {
|
|||
uploadFromComputerLimit: 'Carregar arquivo não pode exceder {{size}}',
|
||||
uploadFromComputerUploadError: 'Falha no upload do arquivo, faça o upload novamente.',
|
||||
},
|
||||
license: {
|
||||
expiring: 'Expirando em um dia',
|
||||
expiring_plural: 'Expirando em {{count}} dias',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -99,6 +99,12 @@ const translation = {
|
|||
resetPasswordDesc: 'Digite o e-mail que você usou para se inscrever no Dify e enviaremos um e-mail de redefinição de senha.',
|
||||
sendVerificationCode: 'Enviar código de verificação',
|
||||
usePassword: 'Usar senha',
|
||||
licenseInactiveTip: 'A licença do Dify Enterprise para seu espaço de trabalho está inativa. Entre em contato com o administrador para continuar usando o Dify.',
|
||||
licenseLostTip: 'Falha ao conectar o servidor de licenças Dify. Entre em contato com o administrador para continuar usando o Dify.',
|
||||
licenseExpired: 'Licença expirada',
|
||||
licenseLost: 'Licença perdida',
|
||||
licenseInactive: 'Licença inativa',
|
||||
licenseExpiredTip: 'A licença do Dify Enterprise para seu espaço de trabalho expirou. Entre em contato com o administrador para continuar usando o Dify.',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -407,6 +407,7 @@ const translation = {
|
|||
},
|
||||
type: 'Tipo',
|
||||
binaryFileVariable: 'Variável de arquivo binário',
|
||||
extractListPlaceholder: 'Insira o índice do item da lista, digite \'/\' inserir variável',
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Variáveis de entrada',
|
||||
|
@ -618,6 +619,7 @@ const translation = {
|
|||
filterConditionKey: 'Chave de condição do filtro',
|
||||
filterConditionComparisonOperator: 'Operador de comparação de condição de filtro',
|
||||
filterConditionComparisonValue: 'Valor da condição do filtro',
|
||||
extractsCondition: 'Extraia o item N',
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
|
|
|
@ -591,6 +591,10 @@ const translation = {
|
|||
uploadFromComputerLimit: 'Încărcarea fișierului nu poate depăși {{size}}',
|
||||
pasteFileLink: 'Lipiți linkul fișierului',
|
||||
},
|
||||
license: {
|
||||
expiring: 'Expiră într-o zi',
|
||||
expiring_plural: 'Expiră în {{count}} zile',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -99,6 +99,12 @@ const translation = {
|
|||
or: 'SAU',
|
||||
resetPasswordDesc: 'Tastați e-mailul pe care l-ați folosit pentru a vă înscrie pe Dify și vă vom trimite un e-mail de resetare a parolei.',
|
||||
changePasswordBtn: 'Setați o parolă',
|
||||
licenseLostTip: 'Nu s-a reușit conectarea serverului de licențe Dify. Contactați administratorul pentru a continua să utilizați Dify.',
|
||||
licenseInactive: 'Licență inactivă',
|
||||
licenseInactiveTip: 'Licența Dify Enterprise pentru spațiul de lucru este inactivă. Contactați administratorul pentru a continua să utilizați Dify.',
|
||||
licenseExpired: 'Licență expirată',
|
||||
licenseLost: 'Licență pierdută',
|
||||
licenseExpiredTip: 'Licența Dify Enterprise pentru spațiul de lucru a expirat. Contactați administratorul pentru a continua să utilizați Dify.',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -407,6 +407,7 @@ const translation = {
|
|||
},
|
||||
type: 'Tip',
|
||||
binaryFileVariable: 'Variabilă de fișier binar',
|
||||
extractListPlaceholder: 'Introduceți indexul elementelor din listă, tastați "/" inserați variabila',
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Variabile de intrare',
|
||||
|
@ -618,6 +619,7 @@ const translation = {
|
|||
limit: 'N de sus',
|
||||
filterConditionComparisonValue: 'Valoare Stare filtrare',
|
||||
asc: 'ASC',
|
||||
extractsCondition: 'Extrageți elementul N',
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
|
|
|
@ -591,6 +591,10 @@ const translation = {
|
|||
uploadFromComputerLimit: 'Файл загрузки не может превышать {{size}}',
|
||||
uploadFromComputerUploadError: 'Загрузка файла не удалась, пожалуйста, загрузите еще раз.',
|
||||
},
|
||||
license: {
|
||||
expiring: 'Срок действия истекает за один день',
|
||||
expiring_plural: 'Срок действия истекает через {{count}} дней',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -99,6 +99,12 @@ const translation = {
|
|||
noLoginMethodTip: 'Обратитесь к системному администратору, чтобы добавить метод аутентификации.',
|
||||
resetPasswordDesc: 'Введите адрес электронной почты, который вы использовали для регистрации в Dify, и мы отправим вам электронное письмо для сброса пароля.',
|
||||
or: 'ИЛИ',
|
||||
licenseInactive: 'Лицензия неактивна',
|
||||
licenseLostTip: 'Не удалось подключить сервер лицензий Dify. Обратитесь к своему администратору, чтобы продолжить использование Dify.',
|
||||
licenseExpired: 'Срок действия лицензии истек',
|
||||
licenseLost: 'Утеряна лицензия',
|
||||
licenseInactiveTip: 'Лицензия Dify Enterprise для рабочего пространства неактивна. Обратитесь к своему администратору, чтобы продолжить использование Dify.',
|
||||
licenseExpiredTip: 'Срок действия лицензии Dify Enterprise для рабочего пространства истек. Обратитесь к своему администратору, чтобы продолжить использование Dify.',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -407,6 +407,7 @@ const translation = {
|
|||
},
|
||||
type: 'Тип',
|
||||
binaryFileVariable: 'Переменная двоичного файла',
|
||||
extractListPlaceholder: 'Введите индекс элемента списка, введите \'/\' вставьте переменную',
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Входные переменные',
|
||||
|
@ -618,6 +619,7 @@ const translation = {
|
|||
filterConditionKey: 'Ключ условия фильтра',
|
||||
selectVariableKeyPlaceholder: 'Выбор ключа подпеременной',
|
||||
filterConditionComparisonValue: 'Значение условия фильтра',
|
||||
extractsCondition: 'Извлечение элемента N',
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
|
|
|
@ -38,6 +38,10 @@ const translation = {
|
|||
duplicate: 'Podvoji',
|
||||
rename: 'Preimenuj',
|
||||
audioSourceUnavailable: 'Zvočni vir ni na voljo',
|
||||
copyImage: 'Kopiraj sliko',
|
||||
openInNewTab: 'Odpri v novem zavihku',
|
||||
zoomOut: 'Pomanjšanje',
|
||||
zoomIn: 'Povečava',
|
||||
},
|
||||
errorMsg: {
|
||||
fieldRequired: '{{field}} je obvezno',
|
||||
|
@ -576,5 +580,220 @@ const translation = {
|
|||
failed: 'Ustvarjanje oznake ni uspelo',
|
||||
},
|
||||
},
|
||||
dataSource: {
|
||||
notion: {
|
||||
selector: {
|
||||
pageSelected: 'Izbrane strani',
|
||||
addPages: 'Dodajanje strani',
|
||||
searchPages: 'Iskanje strani ...',
|
||||
noSearchResult: 'Ni rezultatov iskanja',
|
||||
preview: 'PREDOGLED',
|
||||
},
|
||||
connected: 'Povezani',
|
||||
remove: 'Odstrani',
|
||||
addWorkspace: 'Dodajanje delovnega prostora',
|
||||
connectedWorkspace: 'Povezani delovni prostor',
|
||||
description: 'Uporaba pojma kot vira podatkov za znanje.',
|
||||
disconnected: 'Odklopi',
|
||||
pagesAuthorized: 'Dovoljene strani',
|
||||
title: 'Pojem',
|
||||
changeAuthorizedPages: 'Spreminjanje pooblaščenih strani',
|
||||
sync: 'Sinhroniziranje',
|
||||
},
|
||||
website: {
|
||||
active: 'Dejaven',
|
||||
configuredCrawlers: 'Konfigurirani pajki',
|
||||
title: 'Spletna stran',
|
||||
inactive: 'Neaktiven',
|
||||
description: 'Uvozite vsebino s spletnih mest s spletnim pajkom.',
|
||||
with: 'S',
|
||||
},
|
||||
add: 'Dodajanje vira podatkov',
|
||||
connect: 'Povezati',
|
||||
configure: 'Konfigurirati',
|
||||
},
|
||||
plugin: {
|
||||
serpapi: {
|
||||
apiKeyPlaceholder: 'Vnesite ključ API',
|
||||
apiKey: 'API ključ',
|
||||
keyFrom: 'Pridobite svoj ključ SerpAPI na strani računa SerpAPI',
|
||||
},
|
||||
},
|
||||
apiBasedExtension: {
|
||||
selector: {
|
||||
placeholder: 'Prosimo, izberite razširitev API-ja',
|
||||
manage: 'Upravljanje razširitve API',
|
||||
title: 'Razširitev API-ja',
|
||||
},
|
||||
modal: {
|
||||
name: {
|
||||
placeholder: 'Prosimo, vnesite ime',
|
||||
title: 'Ime',
|
||||
},
|
||||
apiEndpoint: {
|
||||
title: 'Končna točka API-ja',
|
||||
placeholder: 'Prosimo, vnesite končno točko API-ja',
|
||||
},
|
||||
apiKey: {
|
||||
lengthError: 'Dolžina ključa API ne sme biti manjša od 5 znakov',
|
||||
title: 'Ključ API-ja',
|
||||
placeholder: 'Prosimo, vnesite API-ključ',
|
||||
},
|
||||
editTitle: 'Uredi razširitev API-ja',
|
||||
title: 'Dodajanje razširitve API-ja',
|
||||
},
|
||||
type: 'Vrsta',
|
||||
link: 'Preberite, kako razvijete lastno razširitev API-ja.',
|
||||
title: 'Razširitve API zagotavljajo centralizirano upravljanje API, kar poenostavlja konfiguracijo za enostavno uporabo v aplikacijah Dify.',
|
||||
linkUrl: 'https://docs.dify.ai/features/extension/api_based_extension',
|
||||
add: 'Dodajanje razširitve API-ja',
|
||||
},
|
||||
about: {
|
||||
updateNow: 'Posodobi zdaj',
|
||||
nowAvailable: 'Dify {{version}} je zdaj na voljo.',
|
||||
latestAvailable: 'Dify {{version}} je najnovejša različica, ki je na voljo.',
|
||||
changeLog: 'Dnevnik sprememb',
|
||||
},
|
||||
appMenus: {
|
||||
apiAccess: 'Dostop do API-ja',
|
||||
logs: 'Dnevniki',
|
||||
logAndAnn: 'Dnevniki & Ann.',
|
||||
promptEng: 'Orkester',
|
||||
overview: 'Spremljanje',
|
||||
},
|
||||
environment: {
|
||||
development: 'RAZVOJ',
|
||||
testing: 'PREIZKUŠANJE',
|
||||
},
|
||||
appModes: {
|
||||
completionApp: 'Generator besedila',
|
||||
chatApp: 'Aplikacija za klepet',
|
||||
},
|
||||
datasetMenus: {
|
||||
documents: 'Dokumentov',
|
||||
settings: 'Nastavitve',
|
||||
hitTesting: 'Testiranje pridobivanja',
|
||||
emptyTip: 'Znanje ni bilo povezano, prosimo, pojdite na aplikacijo ali vtičnik, da dokončate združenje.',
|
||||
viewDoc: 'Oglejte si dokumentacijo',
|
||||
relatedApp: 'Povezane aplikacije',
|
||||
},
|
||||
voiceInput: {
|
||||
notAllow: 'Mikrofon ni pooblaščen',
|
||||
speaking: 'Spregovorite zdaj ...',
|
||||
converting: 'Pretvorba v besedilo ...',
|
||||
},
|
||||
modelName: {
|
||||
'claude-2': 'Claude-2',
|
||||
'gpt-4-32k': 'GPT-4-32K',
|
||||
'text-embedding-ada-002': 'Vdelava besedila-Ada-002',
|
||||
'gpt-4': 'GPT-4',
|
||||
'whisper-1': 'Šepet-1',
|
||||
'claude-instant-1': 'Claude-Instant',
|
||||
'text-davinci-003': 'Besedilo-Davinci-003',
|
||||
'gpt-3.5-turbo-16k': 'GPT-3.5-Turbo-16K',
|
||||
'gpt-3.5-turbo': 'GPT-3.5-Turbo',
|
||||
},
|
||||
chat: {
|
||||
citation: {
|
||||
vectorHash: 'Vektorska razpršitev:',
|
||||
hitScore: 'Rezultat pridobivanja:',
|
||||
linkToDataset: 'Povezava do znanja',
|
||||
hitCount: 'Število pridobivanja:',
|
||||
characters: 'Znakov:',
|
||||
title: 'CITATI',
|
||||
},
|
||||
conversationNameCanNotEmpty: 'Zahtevano ime pogovora',
|
||||
inputPlaceholder: 'Pogovorite se z botom',
|
||||
renameConversation: 'Preimenovanje pogovora',
|
||||
conversationName: 'Ime pogovora',
|
||||
conversationNamePlaceholder: 'Prosimo, vnesite ime pogovora',
|
||||
},
|
||||
promptEditor: {
|
||||
context: {
|
||||
item: {
|
||||
desc: 'Vstavljanje predloge konteksta',
|
||||
title: 'Kontekstu',
|
||||
},
|
||||
modal: {
|
||||
footer: 'Kontekste lahko upravljate v spodnjem razdelku Kontekst.',
|
||||
title: '{{num}} Znanje v kontekstu',
|
||||
add: 'Dodajanje konteksta',
|
||||
},
|
||||
},
|
||||
history: {
|
||||
item: {
|
||||
desc: 'Vstavljanje predloge zgodovinskega sporočila',
|
||||
title: 'Zgodovina pogovorov',
|
||||
},
|
||||
modal: {
|
||||
title: 'PRIMER',
|
||||
edit: 'Urejanje imen vlog v pogovoru',
|
||||
assistant: 'Zdravo! Kako vam lahko pomagam danes?',
|
||||
user: 'Zdravo',
|
||||
},
|
||||
},
|
||||
variable: {
|
||||
item: {
|
||||
desc: 'Vstavljanje spremenljivk in zunanjih orodij',
|
||||
title: 'Spremenljivke in zunanja orodja',
|
||||
},
|
||||
outputToolDisabledItem: {
|
||||
title: 'Spremenljivke',
|
||||
desc: 'Vstavljanje spremenljivk',
|
||||
},
|
||||
modal: {
|
||||
addTool: 'Novo orodje',
|
||||
add: 'Nova spremenljivka',
|
||||
},
|
||||
},
|
||||
query: {
|
||||
item: {
|
||||
title: 'Poizvedba',
|
||||
desc: 'Vstavljanje predloge uporabniške poizvedbe',
|
||||
},
|
||||
},
|
||||
existed: 'Že obstaja v pozivu',
|
||||
placeholder: 'Tukaj vnesite svojo pozivno besedo, vnesite \'{\' za vstavljanje spremenljivke, vnesite \'/\', da vstavite blok vsebine',
|
||||
},
|
||||
imageUploader: {
|
||||
pasteImageLinkInvalid: 'Neveljavna povezava do slike',
|
||||
uploadFromComputerLimit: 'Nalaganje slik ne sme presegati {{size}} MB',
|
||||
uploadFromComputerUploadError: 'Nalaganje slike ni uspelo, naložite ga znova.',
|
||||
pasteImageLink: 'Prilepi povezavo do slike',
|
||||
imageUpload: 'Nalaganje slik',
|
||||
uploadFromComputer: 'Naloži iz računalnika',
|
||||
pasteImageLinkInputPlaceholder: 'Tukaj prilepi povezavo do slike',
|
||||
uploadFromComputerReadError: 'Branje slik ni uspelo, poskusite znova.',
|
||||
},
|
||||
fileUploader: {
|
||||
fileExtensionNotSupport: 'Datotečna pripona ni podprta',
|
||||
pasteFileLinkInvalid: 'Neveljavna povezava do datoteke',
|
||||
pasteFileLink: 'Prilepi povezavo do datoteke',
|
||||
pasteFileLinkInputPlaceholder: 'Vnesite URL ...',
|
||||
uploadFromComputerUploadError: 'Nalaganje datoteke ni uspelo, naložite ga znova.',
|
||||
uploadFromComputer: 'Lokalno nalaganje',
|
||||
uploadFromComputerLimit: 'Nalaganje {{type}} ne sme presegati {{size}}',
|
||||
uploadFromComputerReadError: 'Branje datoteke ni uspelo, poskusite znova.',
|
||||
},
|
||||
tag: {
|
||||
addTag: 'Dodajanje oznak',
|
||||
delete: 'Brisanje oznake',
|
||||
manageTags: 'Upravljanje oznak',
|
||||
addNew: 'Dodajanje nove oznake',
|
||||
placeholder: 'Vse oznake',
|
||||
failed: 'Ustvarjanje oznake ni uspelo',
|
||||
editTag: 'Urejanje oznak',
|
||||
created: 'Oznaka je bila uspešno ustvarjena',
|
||||
noTagYet: 'Še ni oznak',
|
||||
create: 'Ustvariti',
|
||||
deleteTip: 'Oznaka se uporablja, jo izbrišite?',
|
||||
noTag: 'Brez oznak',
|
||||
selectorPlaceholder: 'Vnesite za iskanje ali ustvarjanje',
|
||||
},
|
||||
license: {
|
||||
expiring_plural: 'Poteče v {{count}} dneh',
|
||||
expiring: 'Poteče v enem dnevu',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -152,6 +152,7 @@ const translation = {
|
|||
indexSettingTip: 'Če želite spremeniti način indeksiranja in model vdelave, pojdite na ',
|
||||
retrievalSettingTip: 'Če želite spremeniti nastavitve iskanja, pojdite na ',
|
||||
datasetSettingLink: 'nastavitve Znanja.',
|
||||
maxLengthCheck: 'Največja dolžina kosa mora biti manjša od 4000',
|
||||
},
|
||||
stepThree: {
|
||||
creationTitle: '🎉 Znanje ustvarjeno',
|
||||
|
|
|
@ -55,6 +55,7 @@ const translation = {
|
|||
passwordEmpty: 'Geslo je obvezno',
|
||||
passwordLengthInValid: 'Geslo mora vsebovati vsaj 8 znakov',
|
||||
passwordInvalid: 'Geslo mora vsebovati črke in številke, dolžina pa mora biti več kot 8 znakov',
|
||||
registrationNotAllowed: 'Računa ni mogoče najti. Za registracijo se obrnite na skrbnika sistema.',
|
||||
},
|
||||
license: {
|
||||
tip: 'Preden začnete z Dify Community Edition, preberite GitHub',
|
||||
|
@ -70,6 +71,40 @@ const translation = {
|
|||
activated: 'Prijavite se zdaj',
|
||||
adminInitPassword: 'Geslo za inicializacijo administratorja',
|
||||
validate: 'Potrdi',
|
||||
checkCode: {
|
||||
emptyCode: 'Koda je obvezna',
|
||||
verificationCodePlaceholder: 'Vnesite 6-mestno kodo',
|
||||
resend: 'Poslati',
|
||||
verificationCode: 'Koda za preverjanje',
|
||||
tips: 'Kodo za preverjanje pošljemo na <strong>{{email}}</strong>',
|
||||
verify: 'Preveriti',
|
||||
validTime: 'Upoštevajte, da je koda veljavna 5 minut',
|
||||
checkYourEmail: 'Preverjanje e-pošte',
|
||||
didNotReceiveCode: 'Niste prejeli kode?',
|
||||
invalidCode: 'Neveljavna koda',
|
||||
useAnotherMethod: 'Uporabite drug način',
|
||||
},
|
||||
useVerificationCode: 'Uporaba kode za preverjanje',
|
||||
licenseInactive: 'Licenca je neaktivna',
|
||||
changePasswordBtn: 'Nastavitev gesla',
|
||||
licenseExpired: 'Licenca je potekla',
|
||||
resetPassword: 'Ponastavi geslo',
|
||||
back: 'Hrbet',
|
||||
backToLogin: 'Nazaj na prijavo',
|
||||
enterYourName: 'Prosimo, vnesite svoje uporabniško ime',
|
||||
licenseLost: 'Izgubljena licenca',
|
||||
licenseExpiredTip: 'Licenca za Dify Enterprise za vaš delovni prostor je potekla. Če želite še naprej uporabljati Dify, se obrnite na skrbnika.',
|
||||
usePassword: 'Uporaba gesla',
|
||||
sendVerificationCode: 'Pošlji kodo za preverjanje',
|
||||
resetPasswordDesc: 'Vnesite e-poštni naslov, ki ste ga uporabili za prijavo na Dify, in poslali vam bomo e-poštno sporočilo za ponastavitev gesla.',
|
||||
setYourAccount: 'Nastavitev računa',
|
||||
noLoginMethodTip: 'Obrnite se na skrbnika sistema, da dodate način preverjanja pristnosti.',
|
||||
or: 'ALI',
|
||||
noLoginMethod: 'Način preverjanja pristnosti ni konfiguriran',
|
||||
continueWithCode: 'Nadaljujte s kodo',
|
||||
withSSO: 'Nadaljujte z enotno prijavo',
|
||||
licenseLostTip: 'Povezava z licenčnim strežnikom Dify ni uspela. Če želite še naprej uporabljati Dify, se obrnite na skrbnika.',
|
||||
licenseInactiveTip: 'Licenca Dify Enterprise za vaš delovni prostor je neaktivna. Če želite še naprej uporabljati Dify, se obrnite na skrbnika.',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -95,6 +95,10 @@ const translation = {
|
|||
addParallelNode: 'Dodaj vzporedno vozlišče',
|
||||
parallel: 'VZPOREDNO',
|
||||
branch: 'VEJA',
|
||||
fileUploadTip: 'Funkcije nalaganja slik so nadgrajene na nalaganje datotek.',
|
||||
featuresDocLink: 'Izvedi več',
|
||||
featuresDescription: 'Izboljšajte uporabniško izkušnjo spletne aplikacije',
|
||||
ImageUploadLegacyTip: 'Zdaj lahko ustvarite spremenljivke vrste datoteke v začetnem obrazcu. V prihodnje ne bomo več podpirali funkcije nalaganja slik.',
|
||||
},
|
||||
env: {
|
||||
envPanelTitle: 'Spremenljivke okolja',
|
||||
|
@ -554,6 +558,506 @@ const translation = {
|
|||
tracing: {
|
||||
stopBy: 'Ustavljeno s strani {{user}}',
|
||||
},
|
||||
chatVariable: {
|
||||
modal: {
|
||||
type: 'Vrsta',
|
||||
objectValue: 'Privzeta vrednost',
|
||||
description: 'Opis',
|
||||
editTitle: 'Urejanje spremenljivke pogovora',
|
||||
namePlaceholder: 'Ime spremenljivke',
|
||||
valuePlaceholder: 'Privzeta vrednost, pustite prazno, da ni nastavljeno',
|
||||
title: 'Dodajanje spremenljivke pogovora',
|
||||
editInJSON: 'Urejanje v JSON',
|
||||
value: 'Privzeta vrednost',
|
||||
oneByOne: 'Dodajanje enega za drugim',
|
||||
objectKey: 'Ključ',
|
||||
objectType: 'Vrsta',
|
||||
arrayValue: 'Vrednost',
|
||||
name: 'Ime',
|
||||
descriptionPlaceholder: 'Opis spremenljivke',
|
||||
editInForm: 'Uredi v obrazcu',
|
||||
addArrayValue: 'Dodajanje vrednosti',
|
||||
},
|
||||
storedContent: 'Shranjena vsebina',
|
||||
updatedAt: 'Posodobljeno na',
|
||||
panelTitle: 'Spremenljivke pogovora',
|
||||
button: 'Dodajanje spremenljivke',
|
||||
panelDescription: 'Spremenljivke pogovora se uporabljajo za shranjevanje interaktivnih informacij, ki si jih mora LLM zapomniti, vključno z zgodovino pogovorov, naloženimi datotekami, uporabniškimi nastavitvami. So branje in pisanje.',
|
||||
docLink: 'Če želite izvedeti več, obiščite naše dokumente.',
|
||||
},
|
||||
changeHistory: {
|
||||
nodeChange: 'Blokiranje spremenjeno',
|
||||
placeholder: 'Ničesar še niste spremenili',
|
||||
nodeDescriptionChange: 'Opis bloka je bil spremenjen',
|
||||
nodePaste: 'Blokiranje lepljenja',
|
||||
noteDelete: 'Opomba izbrisana',
|
||||
nodeDragStop: 'Blok premaknjen',
|
||||
nodeConnect: 'Blok povezan',
|
||||
sessionStart: 'Začetek seje',
|
||||
nodeDelete: 'Blokiraj izbrisane',
|
||||
stepBackward_other: '{{count}} stopi nazaj',
|
||||
hint: 'Namig',
|
||||
noteAdd: 'Opomba dodana',
|
||||
clearHistory: 'Počisti zgodovino',
|
||||
stepForward_one: '{{count}} korak naprej',
|
||||
stepBackward_one: '{{count}} korak nazaj',
|
||||
nodeAdd: 'Blokiranje dodano',
|
||||
noteChange: 'Opomba spremenjena',
|
||||
hintText: 'Dejanjem urejanja se sledi v zgodovini sprememb, ki je shranjena v napravi za čas trajanja te seje. Ta zgodovina bo izbrisana, ko zapustite urejevalnik.',
|
||||
stepForward_other: '{{count}} koraki naprej',
|
||||
edgeDelete: 'Blok je prekinjen.',
|
||||
nodeTitleChange: 'Naslov bloka spremenjen',
|
||||
nodeResize: 'Spremeni velikost bloka',
|
||||
title: 'Zgodovina sprememb',
|
||||
currentState: 'Trenutno stanje',
|
||||
},
|
||||
errorMsg: {
|
||||
fields: {
|
||||
code: 'Koda',
|
||||
variableValue: 'Vrednost spremenljivke',
|
||||
visionVariable: 'Spremenljivka vida',
|
||||
model: 'Model',
|
||||
rerankModel: 'Ponovno razvrsti model',
|
||||
variable: 'Ime spremenljivke',
|
||||
},
|
||||
invalidJson: '{{field}} je neveljaven JSON',
|
||||
invalidVariable: 'Neveljavna spremenljivka',
|
||||
authRequired: 'Dovoljenje je potrebno',
|
||||
fieldRequired: '{{field}} je obvezno',
|
||||
rerankModelRequired: 'Preden vklopite Rerank Model, preverite, ali je bil model uspešno konfiguriran v nastavitvah.',
|
||||
},
|
||||
singleRun: {
|
||||
startRun: 'Začni zagnati',
|
||||
running: 'Tek',
|
||||
testRunIteration: 'Ponovitev preskusnega zagona',
|
||||
iteration: 'Ponovitev',
|
||||
back: 'Hrbet',
|
||||
testRun: 'Preskusni zagon',
|
||||
},
|
||||
tabs: {
|
||||
'blocks': 'Bloki',
|
||||
'workflowTool': 'Potek dela',
|
||||
'transform': 'Preoblikovanje',
|
||||
'question-understand': 'Vprašanje razumeti',
|
||||
'builtInTool': 'Vgrajeno',
|
||||
'allTool': 'Ves',
|
||||
'tools': 'Orodja',
|
||||
'logic': 'Logika',
|
||||
'searchBlock': 'Iskalni blok',
|
||||
'noResult': 'Ni najdenega ujemanja',
|
||||
'customTool': 'Običaj',
|
||||
'utilities': 'Utilities',
|
||||
'searchTool': 'Orodje za iskanje',
|
||||
},
|
||||
blocks: {
|
||||
'variable-aggregator': 'Spremenljivi agregator',
|
||||
'code': 'Koda',
|
||||
'parameter-extractor': 'Ekstraktor parametrov',
|
||||
'llm': 'LLM',
|
||||
'knowledge-retrieval': 'Pridobivanje znanja',
|
||||
'answer': 'Odgovoriti',
|
||||
'end': 'Konec',
|
||||
'document-extractor': 'Ekstraktor dokumentov',
|
||||
'assigner': 'Dodeljevalnik spremenljivke',
|
||||
'iteration-start': 'Začetek ponovitve',
|
||||
'template-transform': 'Predloga',
|
||||
'iteration': 'Ponovitev',
|
||||
'start': 'Začetek',
|
||||
'if-else': 'IF/ELSE',
|
||||
'list-operator': 'Operater seznama',
|
||||
'http-request': 'Zahteva HTTP',
|
||||
'variable-assigner': 'Spremenljivi agregator',
|
||||
'question-classifier': 'Klasifikator vprašanj',
|
||||
},
|
||||
blocksAbout: {
|
||||
'document-extractor': 'Uporablja se za razčlenjevanje naloženih dokumentov v besedilno vsebino, ki je zlahka razumljiva LLM.',
|
||||
'list-operator': 'Uporablja se za filtriranje ali razvrščanje vsebine matrike.',
|
||||
'template-transform': 'Pretvorite podatke v niz s sintakso predloge Jinja',
|
||||
'question-classifier': 'Določite pogoje razvrščanja uporabniških vprašanj, LLM lahko določi, kako poteka pogovor na podlagi opisa klasifikacije',
|
||||
'start': 'Določanje začetnih parametrov za zagon poteka dela',
|
||||
'if-else': 'Omogoča razdelitev poteka dela na dve veji glede na pogoje if/else',
|
||||
'knowledge-retrieval': 'Omogoča poizvedovanje po besedilni vsebini, ki je povezana z uporabniškimi vprašanji iz zbirke znanja',
|
||||
'variable-assigner': 'Združite spremenljivke z več vejami v eno spremenljivko za poenoteno konfiguracijo nadaljnjih vozlišč.',
|
||||
'code': 'Izvedite kodo Python ali NodeJS za izvajanje logike po meri',
|
||||
'answer': 'Določanje vsebine odgovora v pogovoru v klepetu',
|
||||
'iteration': 'Izvedite več korakov na predmetu seznama, dokler niso prikazani vsi rezultati.',
|
||||
'http-request': 'Dovoli pošiljanje zahtev strežnika prek protokola HTTP',
|
||||
'end': 'Določanje končne in končne vrste poteka dela',
|
||||
'variable-aggregator': 'Združite spremenljivke z več vejami v eno spremenljivko za poenoteno konfiguracijo nadaljnjih vozlišč.',
|
||||
'parameter-extractor': 'Uporabite LLM za pridobivanje strukturiranih parametrov iz naravnega jezika za klicanje orodij ali zahteve HTTP.',
|
||||
'assigner': 'Vozlišče za dodeljevanje spremenljivk se uporablja za dodeljevanje vrednosti zapisljivim spremenljivkam (kot so spremenljivke pogovora).',
|
||||
'llm': 'Sklicevanje na velike jezikovne modele za odgovarjanje na vprašanja ali obdelavo naravnega jezika',
|
||||
},
|
||||
operator: {
|
||||
zoomOut: 'Pomanjšanje',
|
||||
zoomTo100: 'Povečava na 100 %',
|
||||
zoomToFit: 'Povečaj, da se prilega',
|
||||
zoomIn: 'Povečava',
|
||||
zoomTo50: 'Povečava na 50%',
|
||||
},
|
||||
panel: {
|
||||
helpLink: 'Povezava za pomoč',
|
||||
organizeBlocks: 'Organiziranje blokov',
|
||||
optional: '(neobvezno)',
|
||||
nextStep: 'Naslednji korak',
|
||||
checklist: 'Kontrolni seznam',
|
||||
runThisStep: 'Zaženite ta korak',
|
||||
about: 'Približno',
|
||||
selectNextStep: 'Izberite Naslednji blok',
|
||||
changeBlock: 'Spremeni blok',
|
||||
createdBy: 'Ustvaril',
|
||||
checklistTip: 'Pred objavo se prepričajte, da so vse težave odpravljene',
|
||||
userInputField: 'Uporabniško polje za vnos',
|
||||
checklistResolved: 'Vse težave so odpravljene',
|
||||
addNextStep: 'Dodajanje naslednjega bloka v ta potek dela',
|
||||
change: 'Spremeniti',
|
||||
},
|
||||
nodes: {
|
||||
common: {
|
||||
memory: {
|
||||
conversationRoleName: 'Ime vloge pogovora',
|
||||
memoryTip: 'Nastavitve pomnilnika klepeta',
|
||||
assistant: 'Predpona pomočnika',
|
||||
user: 'Uporabniška predpona',
|
||||
memory: 'Spomin',
|
||||
windowSize: 'Velikost okna',
|
||||
},
|
||||
memories: {
|
||||
tip: 'Pomnilnik klepeta',
|
||||
title: 'Spomine',
|
||||
builtIn: 'Vgrajeno',
|
||||
},
|
||||
outputVars: 'Izhodne spremenljivke',
|
||||
insertVarTip: 'Vstavi spremenljivko',
|
||||
},
|
||||
start: {
|
||||
outputVars: {
|
||||
memories: {
|
||||
content: 'Vsebina sporočila',
|
||||
des: 'Zgodovina pogovorov',
|
||||
type: 'Vrsta sporočila',
|
||||
},
|
||||
query: 'Uporabniški vnos',
|
||||
files: 'Seznam datotek',
|
||||
},
|
||||
required: 'Zahteva',
|
||||
inputField: 'Vnosno polje',
|
||||
noVarTip: 'Nastavitev vhodov, ki jih je mogoče uporabiti v poteku dela',
|
||||
builtInVar: 'Vgrajene spremenljivke',
|
||||
},
|
||||
end: {
|
||||
output: {
|
||||
variable: 'izhodna spremenljivka',
|
||||
type: 'Vrsta izhoda',
|
||||
},
|
||||
type: {
|
||||
'structured': 'Strukturiran',
|
||||
'plain-text': 'Navadno besedilo',
|
||||
'none': 'Nobena',
|
||||
},
|
||||
outputs: 'Izhodov',
|
||||
},
|
||||
answer: {
|
||||
answer: 'Odgovoriti',
|
||||
outputVars: 'Izhodne spremenljivke',
|
||||
},
|
||||
llm: {
|
||||
roleDescription: {
|
||||
assistant: 'Odgovori modela na podlagi sporočil uporabnikov',
|
||||
system: 'Podajte navodila na visoki ravni za pogovor',
|
||||
user: 'Navedite navodila, poizvedbe ali kakršen koli besedilni vnos v model',
|
||||
},
|
||||
resolution: {
|
||||
low: 'Nizek',
|
||||
high: 'Visok',
|
||||
name: 'Resolucija',
|
||||
},
|
||||
outputVars: {
|
||||
usage: 'Informacije o uporabi modela',
|
||||
output: 'Ustvarjanje vsebine',
|
||||
},
|
||||
singleRun: {
|
||||
variable: 'Spremenljivka',
|
||||
},
|
||||
notSetContextInPromptTip: 'Če želite omogočiti funkcijo konteksta, izpolnite kontekstno spremenljivko v PROMPT.',
|
||||
sysQueryInUser: 'sys.query v sporočilu uporabnika je obvezen',
|
||||
model: 'model',
|
||||
files: 'Datoteke',
|
||||
addMessage: 'Dodaj sporočilo',
|
||||
context: 'Kontekstu',
|
||||
variables: 'Spremenljivke',
|
||||
prompt: 'Uren',
|
||||
vision: 'vid',
|
||||
contextTooltip: 'Znanje lahko uvozite kot kontekst',
|
||||
},
|
||||
knowledgeRetrieval: {
|
||||
outputVars: {
|
||||
title: 'Segmentirani naslov',
|
||||
url: 'Segmentirani URL',
|
||||
output: 'Pridobivanje segmentiranih podatkov',
|
||||
icon: 'Segmentirana ikona',
|
||||
metadata: 'Drugi metapodatki',
|
||||
content: 'Segmentirana vsebina',
|
||||
},
|
||||
queryVariable: 'Spremenljivka poizvedbe',
|
||||
knowledge: 'Znanje',
|
||||
},
|
||||
http: {
|
||||
outputVars: {
|
||||
headers: 'JSON seznama glav odgovorov',
|
||||
body: 'Vsebina odgovora',
|
||||
files: 'Seznam datotek',
|
||||
statusCode: 'Koda stanja odgovora',
|
||||
},
|
||||
authorization: {
|
||||
'authorization': 'Dovoljenje',
|
||||
'header': 'Glava',
|
||||
'bearer': 'Nosilec',
|
||||
'api-key-title': 'API ključ',
|
||||
'basic': 'Osnoven',
|
||||
'no-auth': 'Nobena',
|
||||
'custom': 'Običaj',
|
||||
'authorizationType': 'Vrsta dovoljenja',
|
||||
'auth-type': 'Vrsta preverjanja pristnosti',
|
||||
'api-key': 'Ključ API-ja',
|
||||
},
|
||||
timeout: {
|
||||
readPlaceholder: 'Vnos časovne omejitve branja v sekundah',
|
||||
writePlaceholder: 'Vnesite časovno omejitev pisanja v sekundah',
|
||||
writeLabel: 'Časovna omejitev pisanja',
|
||||
connectLabel: 'Časovna omejitev povezave',
|
||||
title: 'Timeout',
|
||||
readLabel: 'Časovna omejitev branja',
|
||||
connectPlaceholder: 'Vnos časovne omejitve povezave v sekundah',
|
||||
},
|
||||
value: 'Vrednost',
|
||||
key: 'Ključ',
|
||||
notStartWithHttp: 'API se mora začeti z http:// ali https://',
|
||||
body: 'Telo',
|
||||
type: 'Vrsta',
|
||||
inputVars: 'Vhodne spremenljivke',
|
||||
bulkEdit: 'Urejanje v velikem obsegu',
|
||||
insertVarPlaceholder: 'vnesite "/" za vstavljanje spremenljivke',
|
||||
api: 'API',
|
||||
keyValueEdit: 'Urejanje ključ-vrednost',
|
||||
binaryFileVariable: 'Spremenljivka binarne datoteke',
|
||||
headers: 'Glave',
|
||||
apiPlaceholder: 'Vnesite URL, vnesite \'/\' vstavi spremenljivko',
|
||||
extractListPlaceholder: 'Vnesite indeks elementa seznama, vnesite \'/\' vstavi spremenljivko',
|
||||
params: 'Params',
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Vhodne spremenljivke',
|
||||
outputVars: 'Izhodne spremenljivke',
|
||||
searchDependencies: 'Odvisnosti iskanja',
|
||||
advancedDependenciesTip: 'Tukaj dodajte nekaj vnaprej naloženih odvisnosti, ki trajajo dlje časa ali niso privzeto vgrajene',
|
||||
advancedDependencies: 'Napredne odvisnosti',
|
||||
},
|
||||
templateTransform: {
|
||||
outputVars: {
|
||||
output: 'Preoblikovana vsebina',
|
||||
},
|
||||
code: 'Koda',
|
||||
inputVars: 'Vhodne spremenljivke',
|
||||
codeSupportTip: 'Podpira samo Jinja2',
|
||||
},
|
||||
ifElse: {
|
||||
comparisonOperator: {
|
||||
'all of': 'vse',
|
||||
'is not': 'ni',
|
||||
'not empty': 'ni prazen',
|
||||
'start with': 'Začnite z',
|
||||
'is': 'Je',
|
||||
'null': 'je nična',
|
||||
'not exists': 'ne obstaja',
|
||||
'contains': 'Vsebuje',
|
||||
'empty': 'je prazen',
|
||||
'exists': 'Obstaja',
|
||||
'in': 'v',
|
||||
'not contains': 'ne vsebuje',
|
||||
'end with': 'Končaj z',
|
||||
'not in': 'ni v',
|
||||
'not null': 'ni nična',
|
||||
},
|
||||
optionName: {
|
||||
video: 'Video',
|
||||
doc: 'Doc',
|
||||
audio: 'Avdio',
|
||||
image: 'Podoba',
|
||||
url: 'Spletni naslov',
|
||||
localUpload: 'Lokalno nalaganje',
|
||||
},
|
||||
and: 'in',
|
||||
else: 'Drugega',
|
||||
enterValue: 'Vnesite vrednost',
|
||||
elseDescription: 'Uporablja se za določanje logike, ki jo je treba izvesti, ko pogoj if ni izpolnjen.',
|
||||
addCondition: 'Dodajanje pogoja',
|
||||
if: 'Če',
|
||||
select: 'Izbrati',
|
||||
selectVariable: 'Izberite spremenljivko ...',
|
||||
conditionNotSetup: 'Pogoj NI nastavljen',
|
||||
addSubVariable: 'Podspremenljivka',
|
||||
notSetVariable: 'Prosimo, najprej nastavite spremenljivko',
|
||||
operator: 'Operaterja',
|
||||
or: 'ali',
|
||||
},
|
||||
variableAssigner: {
|
||||
type: {
|
||||
string: 'Niz',
|
||||
object: 'Predmet',
|
||||
array: 'Matrika',
|
||||
number: 'Številka',
|
||||
},
|
||||
outputVars: {
|
||||
varDescribe: '{{groupName}} izhod',
|
||||
},
|
||||
addGroup: 'Dodajanje skupine',
|
||||
outputType: 'Vrsta izhoda',
|
||||
title: 'Dodeljevanje spremenljivk',
|
||||
noVarTip: 'Seštevanje spremenljivk, ki jih je treba dodeliti',
|
||||
aggregationGroupTip: 'Če omogočite to funkcijo, lahko združevalnik spremenljivk združi več naborov spremenljivk.',
|
||||
aggregationGroup: 'Združevalna skupina',
|
||||
varNotSet: 'Spremenljivka ni nastavljena',
|
||||
setAssignVariable: 'Nastavitev spremenljivke dodelitve',
|
||||
},
|
||||
assigner: {
|
||||
'writeMode': 'Način pisanja',
|
||||
'plus': 'Plus',
|
||||
'variable': 'Spremenljivka',
|
||||
'clear': 'Jasen',
|
||||
'append': 'Dodaj',
|
||||
'assignedVariable': 'Dodeljena spremenljivka',
|
||||
'setVariable': 'Nastavi spremenljivko',
|
||||
'over-write': 'Prepisati',
|
||||
'writeModeTip': 'Način dodajanja: Na voljo samo za spremenljivke polja.',
|
||||
},
|
||||
tool: {
|
||||
outputVars: {
|
||||
files: {
|
||||
transfer_method: 'Način prenosa. Vrednost je remote_url ali local_file',
|
||||
upload_file_id: 'Naloži ID datoteke',
|
||||
type: 'Vrsta podpore. Zdaj podpiramo samo sliko',
|
||||
url: 'URL slike',
|
||||
title: 'Datoteke, ustvarjene z orodjem',
|
||||
},
|
||||
json: 'JSON, ustvarjen z orodjem',
|
||||
text: 'Vsebina, ustvarjena z orodjem',
|
||||
},
|
||||
inputVars: 'Vhodne spremenljivke',
|
||||
toAuthorize: 'Za odobritev',
|
||||
},
|
||||
questionClassifiers: {
|
||||
outputVars: {
|
||||
className: 'Ime razreda',
|
||||
},
|
||||
instruction: 'Navodilo',
|
||||
classNamePlaceholder: 'Napišite ime svojega razreda',
|
||||
addClass: 'Dodajanje razreda',
|
||||
instructionPlaceholder: 'Napišite navodila',
|
||||
topicName: 'Ime teme',
|
||||
topicPlaceholder: 'Napišite ime teme',
|
||||
class: 'Razred',
|
||||
advancedSetting: 'Napredne nastavitve',
|
||||
model: 'model',
|
||||
inputVars: 'Vhodne spremenljivke',
|
||||
instructionTip: 'Vnesite dodatna navodila, ki bodo klasifikatorju vprašanj pomagala bolje razumeti, kako kategorizirati vprašanja.',
|
||||
},
|
||||
parameterExtractor: {
|
||||
addExtractParameterContent: {
|
||||
description: 'Opis',
|
||||
typePlaceholder: 'Vrsta parametra izvlečka',
|
||||
requiredContent: 'Zahtevano se uporablja samo kot referenca za sklepanje modela in ne za obvezno validacijo izhodnega parametra.',
|
||||
required: 'Zahteva',
|
||||
type: 'Vrsta',
|
||||
namePlaceholder: 'Izvleček imena parametra',
|
||||
descriptionPlaceholder: 'Opis parametra izvlečka',
|
||||
name: 'Ime',
|
||||
},
|
||||
isSuccess: 'Je uspeh.Pri uspehu je vrednost 1, pri neuspehu je vrednost 0.',
|
||||
addExtractParameter: 'Dodajanje parametra izvlečka',
|
||||
importFromTool: 'Uvoz iz orodij',
|
||||
reasoningModeTip: 'Izberete lahko ustrezen način sklepanja glede na sposobnost modela, da se odzove na navodila za klicanje funkcij ali pozive.',
|
||||
inputVar: 'Vhodna spremenljivka',
|
||||
advancedSetting: 'Napredne nastavitve',
|
||||
errorReason: 'Razlog za napako',
|
||||
reasoningMode: 'Način sklepanja',
|
||||
instruction: 'Navodilo',
|
||||
instructionTip: 'Vnesite dodatna navodila, ki bodo ekstraktorju parametrov pomagala razumeti, kako izvleči parametre.',
|
||||
extractParametersNotSet: 'Izvleček parametrov ni nastavljen',
|
||||
extractParameters: 'Izvleček parametrov',
|
||||
},
|
||||
iteration: {
|
||||
ErrorMethod: {
|
||||
continueOnError: 'Nadaljuj ob napaki',
|
||||
removeAbnormalOutput: 'Odstranite nenormalen izhod',
|
||||
operationTerminated: 'Prekinjena',
|
||||
},
|
||||
output: 'Izhodne spremenljivke',
|
||||
parallelMode: 'Vzporedni način',
|
||||
MaxParallelismTitle: 'Največji vzporednost',
|
||||
errorResponseMethod: 'Način odziva na napako',
|
||||
parallelModeEnableDesc: 'V vzporednem načinu opravila v iteracijah podpirajo vzporedno izvajanje. To lahko konfigurirate na plošči z lastnostmi na desni.',
|
||||
error_one: '{{štetje}} Napaka',
|
||||
comma: ',',
|
||||
parallelModeUpper: 'VZPOREDNI NAČIN',
|
||||
parallelModeEnableTitle: 'Vzporedni način omogočen',
|
||||
currentIteration: 'Trenutna ponovitev',
|
||||
error_other: '{{štetje}} Napake',
|
||||
input: 'Vhodni',
|
||||
deleteTitle: 'Izbrisati iteracijsko vozlišče?',
|
||||
parallelPanelDesc: 'V vzporednem načinu opravila v iteraciji podpirajo vzporedno izvajanje.',
|
||||
deleteDesc: 'Če izbrišete iteracijsko vozlišče, boste izbrisali vsa podrejena vozlišča',
|
||||
iteration_other: '{{štetje}} Ponovitev',
|
||||
answerNodeWarningDesc: 'Opozorilo vzporednega načina: Vozlišča za odgovore, dodelitve spremenljivk pogovora in trajne operacije branja / pisanja v iteracijah lahko povzročijo izjeme.',
|
||||
MaxParallelismDesc: 'Največja vzporednost se uporablja za nadzor števila nalog, ki se izvajajo hkrati v eni ponovitvi.',
|
||||
iteration_one: '{{štetje}} Ponovitev',
|
||||
},
|
||||
note: {
|
||||
editor: {
|
||||
medium: 'Srednja',
|
||||
openLink: 'Odprt',
|
||||
showAuthor: 'Pokaži avtorja',
|
||||
bold: 'Smel',
|
||||
strikethrough: 'Prečrtano',
|
||||
large: 'Velik',
|
||||
link: 'Povezava',
|
||||
enterUrl: 'Vnesite URL ...',
|
||||
small: 'Majhen',
|
||||
italic: 'Ležeče',
|
||||
invalidUrl: 'Neveljaven URL',
|
||||
unlink: 'Prekini povezavo',
|
||||
placeholder: 'Napišite svojo opombo ...',
|
||||
bulletList: 'Seznam oznak',
|
||||
},
|
||||
addNote: 'Dodaj opombo',
|
||||
},
|
||||
docExtractor: {
|
||||
outputVars: {
|
||||
text: 'Izvlečeno besedilo',
|
||||
},
|
||||
inputVar: 'Vhodna spremenljivka',
|
||||
learnMore: 'Izvedi več',
|
||||
supportFileTypes: 'Podporne vrste datotek: {{types}}.',
|
||||
},
|
||||
listFilter: {
|
||||
outputVars: {
|
||||
result: 'Rezultat filtriranja',
|
||||
first_record: 'Prvi zapis',
|
||||
last_record: 'Zadnji zapis',
|
||||
},
|
||||
extractsCondition: 'Ekstrahiranje elementa N',
|
||||
selectVariableKeyPlaceholder: 'Izberite ključ podspremenljivke',
|
||||
asc: 'ASC',
|
||||
orderBy: 'Naročite po',
|
||||
filterCondition: 'Pogoj filtra',
|
||||
filterConditionKey: 'Ključ pogoja filtra',
|
||||
desc: 'DESC',
|
||||
limit: 'Vrh N',
|
||||
filterConditionComparisonOperator: 'Operator za primerjavo pogojev filtra',
|
||||
inputVar: 'Vhodna spremenljivka',
|
||||
filterConditionComparisonValue: 'Vrednost pogoja filtra',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -591,6 +591,10 @@ const translation = {
|
|||
pasteFileLinkInvalid: 'Geçersiz dosya bağlantısı',
|
||||
fileExtensionNotSupport: 'Dosya uzantısı desteklenmiyor',
|
||||
},
|
||||
license: {
|
||||
expiring_plural: '{{count}} gün içinde sona eriyor',
|
||||
expiring: 'Bir günde sona eriyor',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -99,6 +99,12 @@ const translation = {
|
|||
noLoginMethodTip: 'Bir kimlik doğrulama yöntemi eklemek için lütfen sistem yöneticisine başvurun.',
|
||||
sendVerificationCode: 'Doğrulama Kodu Gönder',
|
||||
back: 'Geri',
|
||||
licenseExpiredTip: 'Çalışma alanınız için Dify Enterprise lisansının süresi doldu. Dify\'ı kullanmaya devam etmek için lütfen yöneticinizle iletişime geçin.',
|
||||
licenseLostTip: 'Dify lisans sunucusuna bağlanılamadı. Dify\'ı kullanmaya devam etmek için lütfen yöneticinizle iletişime geçin.',
|
||||
licenseInactiveTip: 'Çalışma alanınız için Dify Enterprise lisansı etkin değil. Dify\'ı kullanmaya devam etmek için lütfen yöneticinizle iletişime geçin.',
|
||||
licenseExpired: 'Lisansın Süresi Doldu',
|
||||
licenseLost: 'Lisans Kaybedildi',
|
||||
licenseInactive: 'Lisans Etkin Değil',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -408,6 +408,7 @@ const translation = {
|
|||
},
|
||||
type: 'Tür',
|
||||
binaryFileVariable: 'İkili Dosya Değişkeni',
|
||||
extractListPlaceholder: 'Liste öğesi dizinini girin, \'/\' yazın değişken ekle',
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Giriş Değişkenleri',
|
||||
|
@ -619,6 +620,7 @@ const translation = {
|
|||
filterConditionComparisonValue: 'Filtre Koşulu değeri',
|
||||
selectVariableKeyPlaceholder: 'Alt değişken anahtarını seçin',
|
||||
desc: 'DESC',
|
||||
extractsCondition: 'N öğesini ayıklayın',
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
|
|
|
@ -592,6 +592,10 @@ const translation = {
|
|||
uploadFromComputerReadError: 'Не вдалося прочитати файл, будь ласка, спробуйте ще раз.',
|
||||
uploadFromComputerUploadError: 'Не вдалося завантажити файл, будь ласка, завантажте ще раз.',
|
||||
},
|
||||
license: {
|
||||
expiring: 'Термін дії закінчується за один день',
|
||||
expiring_plural: 'Термін дії закінчується за {{count}} днів',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -99,6 +99,12 @@ const translation = {
|
|||
noLoginMethodTip: 'Будь ласка, зверніться до адміністратора системи, щоб додати метод автентифікації.',
|
||||
resetPasswordDesc: 'Введіть адресу електронної пошти, яку ви використовували для реєстрації на Dify, і ми надішлемо вам електронний лист для скидання пароля.',
|
||||
resetPassword: 'Скинути пароль',
|
||||
licenseLostTip: 'Не вдалося підключити сервер ліцензій Dify. Будь ласка, зверніться до свого адміністратора, щоб продовжити користуватися Dify.',
|
||||
licenseExpired: 'Термін дії ліцензії минув',
|
||||
licenseInactive: 'Ліцензія неактивна',
|
||||
licenseLost: 'Ліцензію втрачено',
|
||||
licenseInactiveTip: 'Ліцензія Dify Enterprise для вашої робочої області неактивна. Будь ласка, зверніться до свого адміністратора, щоб продовжити користуватися Dify.',
|
||||
licenseExpiredTip: 'Термін дії ліцензії Dify Enterprise для вашого робочого простору закінчився. Будь ласка, зверніться до свого адміністратора, щоб продовжити користуватися Dify.',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -407,6 +407,7 @@ const translation = {
|
|||
},
|
||||
type: 'Тип',
|
||||
binaryFileVariable: 'Змінна двійкового файлу',
|
||||
extractListPlaceholder: 'Введіть індекс елемента списку, введіть \'/\' вставити змінну',
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Вхідні змінні',
|
||||
|
@ -618,6 +619,7 @@ const translation = {
|
|||
orderBy: 'Замовити по',
|
||||
filterConditionComparisonOperator: 'Оператор порівняння умов фільтра',
|
||||
filterConditionComparisonValue: 'Значення умови фільтра',
|
||||
extractsCondition: 'Витягніть елемент N',
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
|
|
|
@ -591,6 +591,10 @@ const translation = {
|
|||
uploadFromComputerUploadError: 'Tải lên tệp không thành công, vui lòng tải lên lại.',
|
||||
uploadFromComputerReadError: 'Đọc tệp không thành công, vui lòng thử lại.',
|
||||
},
|
||||
license: {
|
||||
expiring_plural: 'Hết hạn sau {{count}} ngày',
|
||||
expiring: 'Hết hạn trong một ngày',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -99,6 +99,12 @@ const translation = {
|
|||
usePassword: 'Sử dụng mật khẩu',
|
||||
resetPassword: 'Đặt lại mật khẩu',
|
||||
sendVerificationCode: 'Gửi mã xác minh',
|
||||
licenseInactive: 'Giấy phép không hoạt động',
|
||||
licenseLost: 'Mất giấy phép',
|
||||
licenseInactiveTip: 'Giấy phép Dify Enterprise cho không gian làm việc của bạn không hoạt động. Vui lòng liên hệ với quản trị viên của bạn để tiếp tục sử dụng Dify.',
|
||||
licenseExpired: 'Giấy phép đã hết hạn',
|
||||
licenseExpiredTip: 'Giấy phép Dify Enterprise cho không gian làm việc của bạn đã hết hạn. Vui lòng liên hệ với quản trị viên của bạn để tiếp tục sử dụng Dify.',
|
||||
licenseLostTip: 'Không thể kết nối máy chủ cấp phép Dify. Vui lòng liên hệ với quản trị viên của bạn để tiếp tục sử dụng Dify.',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -407,6 +407,7 @@ const translation = {
|
|||
},
|
||||
binaryFileVariable: 'Biến tệp nhị phân',
|
||||
type: 'Kiểu',
|
||||
extractListPlaceholder: 'Nhập chỉ mục mục danh sách, nhập \'/\' chèn biến',
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Biến đầu vào',
|
||||
|
@ -618,6 +619,7 @@ const translation = {
|
|||
filterCondition: 'Điều kiện lọc',
|
||||
asc: 'ASC',
|
||||
filterConditionComparisonOperator: 'Toán tử so sánh điều kiện bộ lọc',
|
||||
extractsCondition: 'Giải nén mục N',
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
|
|
|
@ -591,6 +591,10 @@ const translation = {
|
|||
fileExtensionNotSupport: '不支援檔擴展名',
|
||||
uploadFromComputerLimit: '上傳文件不能超過 {{size}}',
|
||||
},
|
||||
license: {
|
||||
expiring: '將在1天內過期',
|
||||
expiring_plural: '將在 {{count}} 天后過期',
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -99,6 +99,12 @@ const translation = {
|
|||
back: '返回',
|
||||
resetPasswordDesc: '輸入您用於註冊 Dify 的電子郵件,我們將向您發送一封密碼重置電子郵件。',
|
||||
usePassword: '使用密碼',
|
||||
licenseExpiredTip: '您的工作區的 Dify Enterprise 許可證已過期。請聯繫您的管理員以繼續使用 Dify。',
|
||||
licenseExpired: '許可證已過期',
|
||||
licenseLost: '許可證丟失',
|
||||
licenseInactive: '許可證處於非活動狀態',
|
||||
licenseInactiveTip: '您的工作區的 Dify Enterprise 許可證處於非活動狀態。請聯繫您的管理員以繼續使用 Dify。',
|
||||
licenseLostTip: '無法連接 Dify 許可證伺服器。請聯繫您的管理員以繼續使用 Dify。',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
@ -407,6 +407,7 @@ const translation = {
|
|||
},
|
||||
type: '類型',
|
||||
binaryFileVariable: '二進位檔變數',
|
||||
extractListPlaceholder: '輸入清單項索引,鍵入 『/』 插入變數',
|
||||
},
|
||||
code: {
|
||||
inputVars: '輸入變量',
|
||||
|
@ -618,6 +619,7 @@ const translation = {
|
|||
selectVariableKeyPlaceholder: 'Select sub variable key (選擇子變數鍵)',
|
||||
filterConditionComparisonOperator: 'Filter Condition Comparison 運算符',
|
||||
filterConditionKey: '篩選條件鍵',
|
||||
extractsCondition: '提取第 N 項',
|
||||
},
|
||||
},
|
||||
tracing: {
|
||||
|
|
Loading…
Reference in New Issue
Block a user