Przeglądaj źródła

Merge remote-tracking branch 'origin/main' into feat/queue-based-graph-engine

tags/2.0.0-beta.1
-LAN- 1 miesiąc temu
rodzic
commit
9c96b23d55
No account linked to committer's email address
46 zmienionych plików z 435 dodań i 274 usunięć
  1. 1
    1
      Makefile
  2. 1
    0
      api/.env.example
  3. 22
    7
      api/controllers/console/auth/oauth_server.py
  4. 1
    1
      api/core/app/apps/advanced_chat/generate_task_pipeline.py
  5. 8
    8
      api/core/entities/provider_configuration.py
  6. 13
    10
      api/core/helper/position_helper.py
  7. 4
    0
      api/core/mcp/client/streamable_client.py
  8. 1
    1
      api/core/mcp/server/streamable_http.py
  9. 2
    2
      api/core/rag/datasource/vdb/myscale/myscale_vector.py
  10. 3
    2
      api/extensions/storage/clickzetta_volume/file_lifecycle.py
  11. 1
    1
      api/pyproject.toml
  12. 64
    1
      api/tests/unit_tests/core/mcp/server/test_streamable_http.py
  13. 1
    1
      api/uv.lock
  14. 71
    38
      docker/docker-compose-template.yaml
  15. 71
    38
      docker/docker-compose.yaml
  16. 19
    16
      web/app/components/base/chat/chat-with-history/chat-wrapper.tsx
  17. 1
    1
      web/app/components/header/account-setting/model-provider-page/declarations.ts
  18. 2
    2
      web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx
  19. 4
    4
      web/app/components/workflow/nodes/_base/components/form-input-item.tsx
  20. 1
    0
      web/i18n/de-DE/common.ts
  21. 1
    0
      web/i18n/fa-IR/common.ts
  22. 1
    0
      web/i18n/hi-IN/common.ts
  23. 10
    10
      web/i18n/id-ID/app-annotation.ts
  24. 13
    13
      web/i18n/id-ID/app-api.ts
  25. 4
    4
      web/i18n/id-ID/app-debug.ts
  26. 8
    8
      web/i18n/id-ID/app-log.ts
  27. 2
    2
      web/i18n/id-ID/app-overview.ts
  28. 8
    8
      web/i18n/id-ID/app.ts
  29. 18
    18
      web/i18n/id-ID/common.ts
  30. 9
    9
      web/i18n/id-ID/custom.ts
  31. 10
    10
      web/i18n/id-ID/dataset-creation.ts
  32. 2
    2
      web/i18n/id-ID/dataset-hit-testing.ts
  33. 1
    1
      web/i18n/id-ID/dataset-settings.ts
  34. 2
    2
      web/i18n/id-ID/dataset.ts
  35. 1
    1
      web/i18n/id-ID/education.ts
  36. 4
    4
      web/i18n/id-ID/explore.ts
  37. 13
    13
      web/i18n/id-ID/login.ts
  38. 5
    5
      web/i18n/id-ID/oauth.ts
  39. 5
    5
      web/i18n/id-ID/plugin.ts
  40. 8
    8
      web/i18n/id-ID/time.ts
  41. 13
    13
      web/i18n/id-ID/workflow.ts
  42. 1
    0
      web/i18n/ro-RO/common.ts
  43. 1
    0
      web/i18n/th-TH/common.ts
  44. 1
    1
      web/i18n/tr-TR/common.ts
  45. 1
    1
      web/i18n/uk-UA/common.ts
  46. 2
    2
      web/package.json

+ 1
- 1
Makefile Wyświetl plik

@@ -30,7 +30,7 @@ prepare-web:
prepare-api:
@echo "🔧 Setting up API environment..."
@cp -n api/.env.example api/.env 2>/dev/null || echo "API .env already exists"
@cd api && uv sync --dev --extra all
@cd api && uv sync --dev
@cd api && uv run flask db upgrade
@echo "✅ API environment prepared (not started)"


+ 1
- 0
api/.env.example Wyświetl plik

@@ -75,6 +75,7 @@ DB_PASSWORD=difyai123456
DB_HOST=localhost
DB_PORT=5432
DB_DATABASE=dify
SQLALCHEMY_POOL_PRE_PING=true

# Storage configuration
# use for store upload files, private keys...

+ 22
- 7
api/controllers/console/auth/oauth_server.py Wyświetl plik

@@ -2,7 +2,7 @@ from functools import wraps
from typing import cast

import flask_login
from flask import request
from flask import jsonify, request
from flask_restx import Resource, reqparse
from werkzeug.exceptions import BadRequest, NotFound

@@ -46,23 +46,38 @@ def oauth_server_access_token_required(view):

authorization_header = request.headers.get("Authorization")
if not authorization_header:
raise BadRequest("Authorization header is required")
response = jsonify({"error": "Authorization header is required"})
response.status_code = 401
response.headers["WWW-Authenticate"] = "Bearer"
return response

parts = authorization_header.strip().split(" ")
parts = authorization_header.strip().split(None, 1)
if len(parts) != 2:
raise BadRequest("Invalid Authorization header format")
response = jsonify({"error": "Invalid Authorization header format"})
response.status_code = 401
response.headers["WWW-Authenticate"] = "Bearer"
return response

token_type = parts[0].strip()
if token_type.lower() != "bearer":
raise BadRequest("token_type is invalid")
response = jsonify({"error": "token_type is invalid"})
response.status_code = 401
response.headers["WWW-Authenticate"] = "Bearer"
return response

access_token = parts[1].strip()
if not access_token:
raise BadRequest("access_token is required")
response = jsonify({"error": "access_token is required"})
response.status_code = 401
response.headers["WWW-Authenticate"] = "Bearer"
return response

account = OAuthServerService.validate_oauth_access_token(oauth_provider_app.client_id, access_token)
if not account:
raise BadRequest("access_token or client_id is invalid")
response = jsonify({"error": "access_token or client_id is invalid"})
response.status_code = 401
response.headers["WWW-Authenticate"] = "Bearer"
return response

kwargs["account"] = account


+ 1
- 1
api/core/app/apps/advanced_chat/generate_task_pipeline.py Wyświetl plik

@@ -305,7 +305,7 @@ class AdvancedChatAppGenerateTaskPipeline:
err = self._base_task_pipeline._handle_error(event=event, session=session, message_id=self._message_id)
yield self._base_task_pipeline._error_to_stream_response(err)

def _handle_workflow_started_event(self, **kwargs) -> Generator[StreamResponse, None, None]:
def _handle_workflow_started_event(self, *args, **kwargs) -> Generator[StreamResponse, None, None]:
"""Handle workflow started events."""
with self._database_session() as session:
workflow_execution = self._workflow_cycle_manager.handle_workflow_run_start()

+ 8
- 8
api/core/entities/provider_configuration.py Wyświetl plik

@@ -410,10 +410,9 @@ class ProviderConfiguration(BaseModel):
:return:
"""
with Session(db.engine) as session:
if credential_name and self._check_provider_credential_name_exists(
credential_name=credential_name, session=session
):
raise ValueError(f"Credential with name '{credential_name}' already exists.")
if credential_name:
if self._check_provider_credential_name_exists(credential_name=credential_name, session=session):
raise ValueError(f"Credential with name '{credential_name}' already exists.")
else:
credential_name = self._generate_provider_credential_name(session)

@@ -891,10 +890,11 @@ class ProviderConfiguration(BaseModel):
:return:
"""
with Session(db.engine) as session:
if credential_name and self._check_custom_model_credential_name_exists(
model=model, model_type=model_type, credential_name=credential_name, session=session
):
raise ValueError(f"Model credential with name '{credential_name}' already exists for {model}.")
if credential_name:
if self._check_custom_model_credential_name_exists(
model=model, model_type=model_type, credential_name=credential_name, session=session
):
raise ValueError(f"Model credential with name '{credential_name}' already exists for {model}.")
else:
credential_name = self._generate_custom_model_credential_name(
model=model, model_type=model_type, session=session

+ 13
- 10
api/core/helper/position_helper.py Wyświetl plik

@@ -1,7 +1,7 @@
import os
from collections import OrderedDict
from collections.abc import Callable
from typing import Any
from typing import TypeVar

from configs import dify_config
from core.tools.utils.yaml_utils import load_yaml_file
@@ -72,11 +72,14 @@ def pin_position_map(original_position_map: dict[str, int], pin_list: list[str])
return position_map


T = TypeVar("T")


def is_filtered(
include_set: set[str],
exclude_set: set[str],
data: Any,
name_func: Callable[[Any], str],
data: T,
name_func: Callable[[T], str],
) -> bool:
"""
Check if the object should be filtered out.
@@ -103,9 +106,9 @@ def is_filtered(

def sort_by_position_map(
position_map: dict[str, int],
data: list[Any],
name_func: Callable[[Any], str],
) -> list[Any]:
data: list[T],
name_func: Callable[[T], str],
):
"""
Sort the objects by the position map.
If the name of the object is not in the position map, it will be put at the end.
@@ -122,9 +125,9 @@ def sort_by_position_map(

def sort_to_dict_by_position_map(
position_map: dict[str, int],
data: list[Any],
name_func: Callable[[Any], str],
) -> OrderedDict[str, Any]:
data: list[T],
name_func: Callable[[T], str],
):
"""
Sort the objects into a ordered dict by the position map.
If the name of the object is not in the position map, it will be put at the end.
@@ -134,4 +137,4 @@ def sort_to_dict_by_position_map(
:return: an OrderedDict with the sorted pairs of name and object
"""
sorted_items = sort_by_position_map(position_map, data, name_func)
return OrderedDict([(name_func(item), item) for item in sorted_items])
return OrderedDict((name_func(item), item) for item in sorted_items)

+ 4
- 0
api/core/mcp/client/streamable_client.py Wyświetl plik

@@ -246,6 +246,10 @@ class StreamableHTTPTransport:
logger.debug("Received 202 Accepted")
return

if response.status_code == 204:
logger.debug("Received 204 No Content")
return

if response.status_code == 404:
if isinstance(message.root, JSONRPCRequest):
self._send_session_terminated_error(

+ 1
- 1
api/core/mcp/server/streamable_http.py Wyświetl plik

@@ -258,5 +258,5 @@ def convert_input_form_to_parameters(
parameters[item.variable]["type"] = "string"
parameters[item.variable]["enum"] = item.options
elif item.type == VariableEntityType.NUMBER:
parameters[item.variable]["type"] = "float"
parameters[item.variable]["type"] = "number"
return parameters, required

+ 2
- 2
api/core/rag/datasource/vdb/myscale/myscale_vector.py Wyświetl plik

@@ -152,8 +152,8 @@ class MyScaleVector(BaseVector):
)
for r in self._client.query(sql).named_results()
]
except Exception as e:
logger.exception("\033[91m\033[1m%s\033[0m \033[95m%s\033[0m", type(e), str(e)) # noqa:TRY401
except Exception:
logger.exception("Vector search operation failed")
return []

def delete(self) -> None:

+ 3
- 2
api/extensions/storage/clickzetta_volume/file_lifecycle.py Wyświetl plik

@@ -1,7 +1,8 @@
"""ClickZetta Volume file lifecycle management

This module provides file lifecycle management features including version control, automatic cleanup, backup and restore
Supports complete lifecycle management for knowledge base files.
This module provides file lifecycle management features including version control,
automatic cleanup, backup and restore. Supports complete lifecycle management for
knowledge base files.
"""

import json

+ 1
- 1
api/pyproject.toml Wyświetl plik

@@ -1,6 +1,6 @@
[project]
name = "dify-api"
version = "1.8.0"
version = "1.8.1"
requires-python = ">=3.11,<3.13"

dependencies = [

+ 64
- 1
api/tests/unit_tests/core/mcp/server/test_streamable_http.py Wyświetl plik

@@ -1,6 +1,7 @@
import json
from unittest.mock import Mock, patch

import jsonschema
import pytest

from core.app.app_config.entities import VariableEntity, VariableEntityType
@@ -434,7 +435,7 @@ class TestUtilityFunctions:
assert parameters["category"]["enum"] == ["A", "B", "C"]

assert "count" in parameters
assert parameters["count"]["type"] == "float"
assert parameters["count"]["type"] == "number"

# FILE type should be skipped - it creates empty dict but gets filtered later
# Check that it doesn't have any meaningful content
@@ -447,3 +448,65 @@ class TestUtilityFunctions:
assert "category" not in required

# Note: _get_request_id function has been removed as request_id is now passed as parameter

def test_convert_input_form_to_parameters_jsonschema_validation_ok(self):
"""Current schema uses 'number' for numeric fields; it should be a valid JSON Schema."""
user_input_form = [
VariableEntity(
type=VariableEntityType.NUMBER,
variable="count",
description="Count",
label="Count",
required=True,
),
VariableEntity(
type=VariableEntityType.TEXT_INPUT,
variable="name",
description="User name",
label="Name",
required=False,
),
]

parameters_dict = {
"count": "Enter count",
"name": "Enter your name",
}

parameters, required = convert_input_form_to_parameters(user_input_form, parameters_dict)

# Build a complete JSON Schema
schema = {
"type": "object",
"properties": parameters,
"required": required,
}

# 1) The schema itself must be valid
jsonschema.Draft202012Validator.check_schema(schema)

# 2) Both float and integer instances should pass validation
jsonschema.validate(instance={"count": 3.14, "name": "alice"}, schema=schema)
jsonschema.validate(instance={"count": 2, "name": "bob"}, schema=schema)

def test_legacy_float_type_schema_is_invalid(self):
"""Legacy/buggy behavior: using 'float' should produce an invalid JSON Schema."""
# Manually construct a legacy/incorrect schema (simulating old behavior)
bad_schema = {
"type": "object",
"properties": {
"count": {
"type": "float", # Invalid type: JSON Schema does not support 'float'
"description": "Enter count",
}
},
"required": ["count"],
}

# The schema itself should raise a SchemaError
with pytest.raises(jsonschema.exceptions.SchemaError):
jsonschema.Draft202012Validator.check_schema(bad_schema)

# Or validation should also raise SchemaError
with pytest.raises(jsonschema.exceptions.SchemaError):
jsonschema.validate(instance={"count": 1.23}, schema=bad_schema)

+ 1
- 1
api/uv.lock Wyświetl plik

@@ -1260,7 +1260,7 @@ wheels = [

[[package]]
name = "dify-api"
version = "1.8.0"
version = "1.8.1"
source = { virtual = "." }
dependencies = [
{ name = "arize-phoenix-otel" },

+ 71
- 38
docker/docker-compose-template.yaml Wyświetl plik

@@ -2,7 +2,7 @@ x-shared-env: &shared-api-worker-env
services:
# API service
api:
image: langgenius/dify-api:1.8.0
image: langgenius/dify-api:1.8.1
restart: always
environment:
# Use the shared environment variables.
@@ -31,7 +31,7 @@ services:
# worker service
# The Celery worker for processing the queue.
worker:
image: langgenius/dify-api:1.8.0
image: langgenius/dify-api:1.8.1
restart: always
environment:
# Use the shared environment variables.
@@ -58,7 +58,7 @@ services:
# worker_beat service
# Celery beat for scheduling periodic tasks.
worker_beat:
image: langgenius/dify-api:1.8.0
image: langgenius/dify-api:1.8.1
restart: always
environment:
# Use the shared environment variables.
@@ -76,7 +76,7 @@ services:

# Frontend web application.
web:
image: langgenius/dify-web:1.8.0
image: langgenius/dify-web:1.8.1
restart: always
environment:
CONSOLE_API_URL: ${CONSOLE_API_URL:-}
@@ -118,7 +118,17 @@ services:
volumes:
- ./volumes/db/data:/var/lib/postgresql/data
healthcheck:
test: [ 'CMD', 'pg_isready', '-h', 'db', '-U', '${PGUSER:-postgres}', '-d', '${POSTGRES_DB:-dify}' ]
test:
[
"CMD",
"pg_isready",
"-h",
"db",
"-U",
"${PGUSER:-postgres}",
"-d",
"${POSTGRES_DB:-dify}",
]
interval: 1s
timeout: 3s
retries: 60
@@ -135,7 +145,11 @@ services:
# Set the redis password when startup redis server.
command: redis-server --requirepass ${REDIS_PASSWORD:-difyai123456}
healthcheck:
test: [ 'CMD-SHELL', 'redis-cli -a ${REDIS_PASSWORD:-difyai123456} ping | grep -q PONG' ]
test:
[
"CMD-SHELL",
"redis-cli -a ${REDIS_PASSWORD:-difyai123456} ping | grep -q PONG",
]

# The DifySandbox
sandbox:
@@ -157,7 +171,7 @@ services:
- ./volumes/sandbox/dependencies:/dependencies
- ./volumes/sandbox/conf:/conf
healthcheck:
test: [ 'CMD', 'curl', '-f', 'http://localhost:8194/health' ]
test: ["CMD", "curl", "-f", "http://localhost:8194/health"]
networks:
- ssrf_proxy_network

@@ -231,7 +245,12 @@ services:
volumes:
- ./ssrf_proxy/squid.conf.template:/etc/squid/squid.conf.template
- ./ssrf_proxy/docker-entrypoint.sh:/docker-entrypoint-mount.sh
entrypoint: [ 'sh', '-c', "cp /docker-entrypoint-mount.sh /docker-entrypoint.sh && sed -i 's/\r$$//' /docker-entrypoint.sh && chmod +x /docker-entrypoint.sh && /docker-entrypoint.sh" ]
entrypoint:
[
"sh",
"-c",
"cp /docker-entrypoint-mount.sh /docker-entrypoint.sh && sed -i 's/\r$$//' /docker-entrypoint.sh && chmod +x /docker-entrypoint.sh && /docker-entrypoint.sh",
]
environment:
# pls clearly modify the squid env vars to fit your network environment.
HTTP_PORT: ${SSRF_HTTP_PORT:-3128}
@@ -260,8 +279,8 @@ services:
- CERTBOT_EMAIL=${CERTBOT_EMAIL}
- CERTBOT_DOMAIN=${CERTBOT_DOMAIN}
- CERTBOT_OPTIONS=${CERTBOT_OPTIONS:-}
entrypoint: [ '/docker-entrypoint.sh' ]
command: [ 'tail', '-f', '/dev/null' ]
entrypoint: ["/docker-entrypoint.sh"]
command: ["tail", "-f", "/dev/null"]

# The nginx reverse proxy.
# used for reverse proxying the API service and Web service.
@@ -278,7 +297,12 @@ services:
- ./volumes/certbot/conf/live:/etc/letsencrypt/live # cert dir (with certbot container)
- ./volumes/certbot/conf:/etc/letsencrypt
- ./volumes/certbot/www:/var/www/html
entrypoint: [ 'sh', '-c', "cp /docker-entrypoint-mount.sh /docker-entrypoint.sh && sed -i 's/\r$$//' /docker-entrypoint.sh && chmod +x /docker-entrypoint.sh && /docker-entrypoint.sh" ]
entrypoint:
[
"sh",
"-c",
"cp /docker-entrypoint-mount.sh /docker-entrypoint.sh && sed -i 's/\r$$//' /docker-entrypoint.sh && chmod +x /docker-entrypoint.sh && /docker-entrypoint.sh",
]
environment:
NGINX_SERVER_NAME: ${NGINX_SERVER_NAME:-_}
NGINX_HTTPS_ENABLED: ${NGINX_HTTPS_ENABLED:-false}
@@ -300,14 +324,14 @@ services:
- api
- web
ports:
- '${EXPOSE_NGINX_PORT:-80}:${NGINX_PORT:-80}'
- '${EXPOSE_NGINX_SSL_PORT:-443}:${NGINX_SSL_PORT:-443}'
- "${EXPOSE_NGINX_PORT:-80}:${NGINX_PORT:-80}"
- "${EXPOSE_NGINX_SSL_PORT:-443}:${NGINX_SSL_PORT:-443}"

# The Weaviate vector store.
weaviate:
image: semitechnologies/weaviate:1.19.0
profiles:
- ''
- ""
- weaviate
restart: always
volumes:
@@ -360,13 +384,17 @@ services:
working_dir: /opt/couchbase
stdin_open: true
tty: true
entrypoint: [ "" ]
entrypoint: [""]
command: sh -c "/opt/couchbase/init/init-cbserver.sh"
volumes:
- ./volumes/couchbase/data:/opt/couchbase/var/lib/couchbase/data
healthcheck:
# ensure bucket was created before proceeding
test: [ "CMD-SHELL", "curl -s -f -u Administrator:password http://localhost:8091/pools/default/buckets | grep -q '\\[{' || exit 1" ]
test:
[
"CMD-SHELL",
"curl -s -f -u Administrator:password http://localhost:8091/pools/default/buckets | grep -q '\\[{' || exit 1",
]
interval: 10s
retries: 10
start_period: 30s
@@ -392,9 +420,9 @@ services:
volumes:
- ./volumes/pgvector/data:/var/lib/postgresql/data
- ./pgvector/docker-entrypoint.sh:/docker-entrypoint.sh
entrypoint: [ '/docker-entrypoint.sh' ]
entrypoint: ["/docker-entrypoint.sh"]
healthcheck:
test: [ 'CMD', 'pg_isready' ]
test: ["CMD", "pg_isready"]
interval: 1s
timeout: 3s
retries: 30
@@ -411,14 +439,14 @@ services:
- VB_USERNAME=dify
- VB_PASSWORD=Difyai123456
ports:
- '5434:5432'
- "5434:5432"
volumes:
- ./vastbase/lic:/home/vastbase/vastbase/lic
- ./vastbase/data:/home/vastbase/data
- ./vastbase/backup:/home/vastbase/backup
- ./vastbase/backup_log:/home/vastbase/backup_log
healthcheck:
test: [ 'CMD', 'pg_isready' ]
test: ["CMD", "pg_isready"]
interval: 1s
timeout: 3s
retries: 30
@@ -440,7 +468,7 @@ services:
volumes:
- ./volumes/pgvecto_rs/data:/var/lib/postgresql/data
healthcheck:
test: [ 'CMD', 'pg_isready' ]
test: ["CMD", "pg_isready"]
interval: 1s
timeout: 3s
retries: 30
@@ -479,7 +507,11 @@ services:
ports:
- "${OCEANBASE_VECTOR_PORT:-2881}:2881"
healthcheck:
test: [ 'CMD-SHELL', 'obclient -h127.0.0.1 -P2881 -uroot@test -p$${OB_TENANT_PASSWORD} -e "SELECT 1;"' ]
test:
[
"CMD-SHELL",
'obclient -h127.0.0.1 -P2881 -uroot@test -p$${OB_TENANT_PASSWORD} -e "SELECT 1;"',
]
interval: 10s
retries: 30
start_period: 30s
@@ -515,7 +547,7 @@ services:
- ./volumes/milvus/etcd:/etcd
command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
healthcheck:
test: [ 'CMD', 'etcdctl', 'endpoint', 'health' ]
test: ["CMD", "etcdctl", "endpoint", "health"]
interval: 30s
timeout: 20s
retries: 3
@@ -534,7 +566,7 @@ services:
- ./volumes/milvus/minio:/minio_data
command: minio server /minio_data --console-address ":9001"
healthcheck:
test: [ 'CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live' ]
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 20s
retries: 3
@@ -546,7 +578,7 @@ services:
image: milvusdb/milvus:v2.5.15
profiles:
- milvus
command: [ 'milvus', 'run', 'standalone' ]
command: ["milvus", "run", "standalone"]
environment:
ETCD_ENDPOINTS: ${ETCD_ENDPOINTS:-etcd:2379}
MINIO_ADDRESS: ${MINIO_ADDRESS:-minio:9000}
@@ -554,7 +586,7 @@ services:
volumes:
- ./volumes/milvus/milvus:/var/lib/milvus
healthcheck:
test: [ 'CMD', 'curl', '-f', 'http://localhost:9091/healthz' ]
test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"]
interval: 30s
start_period: 90s
timeout: 20s
@@ -620,7 +652,7 @@ services:
volumes:
- ./volumes/opengauss/data:/var/lib/opengauss/data
healthcheck:
test: [ "CMD-SHELL", "netstat -lntp | grep tcp6 > /dev/null 2>&1" ]
test: ["CMD-SHELL", "netstat -lntp | grep tcp6 > /dev/null 2>&1"]
interval: 10s
timeout: 10s
retries: 10
@@ -673,18 +705,19 @@ services:
node.name: dify-es0
discovery.type: single-node
xpack.license.self_generated.type: basic
xpack.security.enabled: 'true'
xpack.security.enrollment.enabled: 'false'
xpack.security.http.ssl.enabled: 'false'
xpack.security.enabled: "true"
xpack.security.enrollment.enabled: "false"
xpack.security.http.ssl.enabled: "false"
ports:
- ${ELASTICSEARCH_PORT:-9200}:9200
deploy:
resources:
limits:
memory: 2g
entrypoint: [ 'sh', '-c', "sh /docker-entrypoint-mount.sh" ]
entrypoint: ["sh", "-c", "sh /docker-entrypoint-mount.sh"]
healthcheck:
test: [ 'CMD', 'curl', '-s', 'http://localhost:9200/_cluster/health?pretty' ]
test:
["CMD", "curl", "-s", "http://localhost:9200/_cluster/health?pretty"]
interval: 30s
timeout: 10s
retries: 50
@@ -702,17 +735,17 @@ services:
environment:
XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: d1a66dfd-c4d3-4a0a-8290-2abcb83ab3aa
NO_PROXY: localhost,127.0.0.1,elasticsearch,kibana
XPACK_SECURITY_ENABLED: 'true'
XPACK_SECURITY_ENROLLMENT_ENABLED: 'false'
XPACK_SECURITY_HTTP_SSL_ENABLED: 'false'
XPACK_FLEET_ISAIRGAPPED: 'true'
XPACK_SECURITY_ENABLED: "true"
XPACK_SECURITY_ENROLLMENT_ENABLED: "false"
XPACK_SECURITY_HTTP_SSL_ENABLED: "false"
XPACK_FLEET_ISAIRGAPPED: "true"
I18N_LOCALE: zh-CN
SERVER_PORT: '5601'
SERVER_PORT: "5601"
ELASTICSEARCH_HOSTS: http://elasticsearch:9200
ports:
- ${KIBANA_PORT:-5601}:5601
healthcheck:
test: [ 'CMD-SHELL', 'curl -s http://localhost:5601 >/dev/null || exit 1' ]
test: ["CMD-SHELL", "curl -s http://localhost:5601 >/dev/null || exit 1"]
interval: 30s
timeout: 10s
retries: 3

+ 71
- 38
docker/docker-compose.yaml Wyświetl plik

@@ -586,7 +586,7 @@ x-shared-env: &shared-api-worker-env
services:
# API service
api:
image: langgenius/dify-api:1.8.0
image: langgenius/dify-api:1.8.1
restart: always
environment:
# Use the shared environment variables.
@@ -615,7 +615,7 @@ services:
# worker service
# The Celery worker for processing the queue.
worker:
image: langgenius/dify-api:1.8.0
image: langgenius/dify-api:1.8.1
restart: always
environment:
# Use the shared environment variables.
@@ -642,7 +642,7 @@ services:
# worker_beat service
# Celery beat for scheduling periodic tasks.
worker_beat:
image: langgenius/dify-api:1.8.0
image: langgenius/dify-api:1.8.1
restart: always
environment:
# Use the shared environment variables.
@@ -660,7 +660,7 @@ services:

# Frontend web application.
web:
image: langgenius/dify-web:1.8.0
image: langgenius/dify-web:1.8.1
restart: always
environment:
CONSOLE_API_URL: ${CONSOLE_API_URL:-}
@@ -702,7 +702,17 @@ services:
volumes:
- ./volumes/db/data:/var/lib/postgresql/data
healthcheck:
test: [ 'CMD', 'pg_isready', '-h', 'db', '-U', '${PGUSER:-postgres}', '-d', '${POSTGRES_DB:-dify}' ]
test:
[
"CMD",
"pg_isready",
"-h",
"db",
"-U",
"${PGUSER:-postgres}",
"-d",
"${POSTGRES_DB:-dify}",
]
interval: 1s
timeout: 3s
retries: 60
@@ -719,7 +729,11 @@ services:
# Set the redis password when startup redis server.
command: redis-server --requirepass ${REDIS_PASSWORD:-difyai123456}
healthcheck:
test: [ 'CMD-SHELL', 'redis-cli -a ${REDIS_PASSWORD:-difyai123456} ping | grep -q PONG' ]
test:
[
"CMD-SHELL",
"redis-cli -a ${REDIS_PASSWORD:-difyai123456} ping | grep -q PONG",
]

# The DifySandbox
sandbox:
@@ -741,7 +755,7 @@ services:
- ./volumes/sandbox/dependencies:/dependencies
- ./volumes/sandbox/conf:/conf
healthcheck:
test: [ 'CMD', 'curl', '-f', 'http://localhost:8194/health' ]
test: ["CMD", "curl", "-f", "http://localhost:8194/health"]
networks:
- ssrf_proxy_network

@@ -815,7 +829,12 @@ services:
volumes:
- ./ssrf_proxy/squid.conf.template:/etc/squid/squid.conf.template
- ./ssrf_proxy/docker-entrypoint.sh:/docker-entrypoint-mount.sh
entrypoint: [ 'sh', '-c', "cp /docker-entrypoint-mount.sh /docker-entrypoint.sh && sed -i 's/\r$$//' /docker-entrypoint.sh && chmod +x /docker-entrypoint.sh && /docker-entrypoint.sh" ]
entrypoint:
[
"sh",
"-c",
"cp /docker-entrypoint-mount.sh /docker-entrypoint.sh && sed -i 's/\r$$//' /docker-entrypoint.sh && chmod +x /docker-entrypoint.sh && /docker-entrypoint.sh",
]
environment:
# pls clearly modify the squid env vars to fit your network environment.
HTTP_PORT: ${SSRF_HTTP_PORT:-3128}
@@ -844,8 +863,8 @@ services:
- CERTBOT_EMAIL=${CERTBOT_EMAIL}
- CERTBOT_DOMAIN=${CERTBOT_DOMAIN}
- CERTBOT_OPTIONS=${CERTBOT_OPTIONS:-}
entrypoint: [ '/docker-entrypoint.sh' ]
command: [ 'tail', '-f', '/dev/null' ]
entrypoint: ["/docker-entrypoint.sh"]
command: ["tail", "-f", "/dev/null"]

# The nginx reverse proxy.
# used for reverse proxying the API service and Web service.
@@ -862,7 +881,12 @@ services:
- ./volumes/certbot/conf/live:/etc/letsencrypt/live # cert dir (with certbot container)
- ./volumes/certbot/conf:/etc/letsencrypt
- ./volumes/certbot/www:/var/www/html
entrypoint: [ 'sh', '-c', "cp /docker-entrypoint-mount.sh /docker-entrypoint.sh && sed -i 's/\r$$//' /docker-entrypoint.sh && chmod +x /docker-entrypoint.sh && /docker-entrypoint.sh" ]
entrypoint:
[
"sh",
"-c",
"cp /docker-entrypoint-mount.sh /docker-entrypoint.sh && sed -i 's/\r$$//' /docker-entrypoint.sh && chmod +x /docker-entrypoint.sh && /docker-entrypoint.sh",
]
environment:
NGINX_SERVER_NAME: ${NGINX_SERVER_NAME:-_}
NGINX_HTTPS_ENABLED: ${NGINX_HTTPS_ENABLED:-false}
@@ -884,14 +908,14 @@ services:
- api
- web
ports:
- '${EXPOSE_NGINX_PORT:-80}:${NGINX_PORT:-80}'
- '${EXPOSE_NGINX_SSL_PORT:-443}:${NGINX_SSL_PORT:-443}'
- "${EXPOSE_NGINX_PORT:-80}:${NGINX_PORT:-80}"
- "${EXPOSE_NGINX_SSL_PORT:-443}:${NGINX_SSL_PORT:-443}"

# The Weaviate vector store.
weaviate:
image: semitechnologies/weaviate:1.19.0
profiles:
- ''
- ""
- weaviate
restart: always
volumes:
@@ -944,13 +968,17 @@ services:
working_dir: /opt/couchbase
stdin_open: true
tty: true
entrypoint: [ "" ]
entrypoint: [""]
command: sh -c "/opt/couchbase/init/init-cbserver.sh"
volumes:
- ./volumes/couchbase/data:/opt/couchbase/var/lib/couchbase/data
healthcheck:
# ensure bucket was created before proceeding
test: [ "CMD-SHELL", "curl -s -f -u Administrator:password http://localhost:8091/pools/default/buckets | grep -q '\\[{' || exit 1" ]
test:
[
"CMD-SHELL",
"curl -s -f -u Administrator:password http://localhost:8091/pools/default/buckets | grep -q '\\[{' || exit 1",
]
interval: 10s
retries: 10
start_period: 30s
@@ -976,9 +1004,9 @@ services:
volumes:
- ./volumes/pgvector/data:/var/lib/postgresql/data
- ./pgvector/docker-entrypoint.sh:/docker-entrypoint.sh
entrypoint: [ '/docker-entrypoint.sh' ]
entrypoint: ["/docker-entrypoint.sh"]
healthcheck:
test: [ 'CMD', 'pg_isready' ]
test: ["CMD", "pg_isready"]
interval: 1s
timeout: 3s
retries: 30
@@ -995,14 +1023,14 @@ services:
- VB_USERNAME=dify
- VB_PASSWORD=Difyai123456
ports:
- '5434:5432'
- "5434:5432"
volumes:
- ./vastbase/lic:/home/vastbase/vastbase/lic
- ./vastbase/data:/home/vastbase/data
- ./vastbase/backup:/home/vastbase/backup
- ./vastbase/backup_log:/home/vastbase/backup_log
healthcheck:
test: [ 'CMD', 'pg_isready' ]
test: ["CMD", "pg_isready"]
interval: 1s
timeout: 3s
retries: 30
@@ -1024,7 +1052,7 @@ services:
volumes:
- ./volumes/pgvecto_rs/data:/var/lib/postgresql/data
healthcheck:
test: [ 'CMD', 'pg_isready' ]
test: ["CMD", "pg_isready"]
interval: 1s
timeout: 3s
retries: 30
@@ -1063,7 +1091,11 @@ services:
ports:
- "${OCEANBASE_VECTOR_PORT:-2881}:2881"
healthcheck:
test: [ 'CMD-SHELL', 'obclient -h127.0.0.1 -P2881 -uroot@test -p$${OB_TENANT_PASSWORD} -e "SELECT 1;"' ]
test:
[
"CMD-SHELL",
'obclient -h127.0.0.1 -P2881 -uroot@test -p$${OB_TENANT_PASSWORD} -e "SELECT 1;"',
]
interval: 10s
retries: 30
start_period: 30s
@@ -1099,7 +1131,7 @@ services:
- ./volumes/milvus/etcd:/etcd
command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
healthcheck:
test: [ 'CMD', 'etcdctl', 'endpoint', 'health' ]
test: ["CMD", "etcdctl", "endpoint", "health"]
interval: 30s
timeout: 20s
retries: 3
@@ -1118,7 +1150,7 @@ services:
- ./volumes/milvus/minio:/minio_data
command: minio server /minio_data --console-address ":9001"
healthcheck:
test: [ 'CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live' ]
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 20s
retries: 3
@@ -1130,7 +1162,7 @@ services:
image: milvusdb/milvus:v2.5.15
profiles:
- milvus
command: [ 'milvus', 'run', 'standalone' ]
command: ["milvus", "run", "standalone"]
environment:
ETCD_ENDPOINTS: ${ETCD_ENDPOINTS:-etcd:2379}
MINIO_ADDRESS: ${MINIO_ADDRESS:-minio:9000}
@@ -1138,7 +1170,7 @@ services:
volumes:
- ./volumes/milvus/milvus:/var/lib/milvus
healthcheck:
test: [ 'CMD', 'curl', '-f', 'http://localhost:9091/healthz' ]
test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"]
interval: 30s
start_period: 90s
timeout: 20s
@@ -1204,7 +1236,7 @@ services:
volumes:
- ./volumes/opengauss/data:/var/lib/opengauss/data
healthcheck:
test: [ "CMD-SHELL", "netstat -lntp | grep tcp6 > /dev/null 2>&1" ]
test: ["CMD-SHELL", "netstat -lntp | grep tcp6 > /dev/null 2>&1"]
interval: 10s
timeout: 10s
retries: 10
@@ -1257,18 +1289,19 @@ services:
node.name: dify-es0
discovery.type: single-node
xpack.license.self_generated.type: basic
xpack.security.enabled: 'true'
xpack.security.enrollment.enabled: 'false'
xpack.security.http.ssl.enabled: 'false'
xpack.security.enabled: "true"
xpack.security.enrollment.enabled: "false"
xpack.security.http.ssl.enabled: "false"
ports:
- ${ELASTICSEARCH_PORT:-9200}:9200
deploy:
resources:
limits:
memory: 2g
entrypoint: [ 'sh', '-c', "sh /docker-entrypoint-mount.sh" ]
entrypoint: ["sh", "-c", "sh /docker-entrypoint-mount.sh"]
healthcheck:
test: [ 'CMD', 'curl', '-s', 'http://localhost:9200/_cluster/health?pretty' ]
test:
["CMD", "curl", "-s", "http://localhost:9200/_cluster/health?pretty"]
interval: 30s
timeout: 10s
retries: 50
@@ -1286,17 +1319,17 @@ services:
environment:
XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: d1a66dfd-c4d3-4a0a-8290-2abcb83ab3aa
NO_PROXY: localhost,127.0.0.1,elasticsearch,kibana
XPACK_SECURITY_ENABLED: 'true'
XPACK_SECURITY_ENROLLMENT_ENABLED: 'false'
XPACK_SECURITY_HTTP_SSL_ENABLED: 'false'
XPACK_FLEET_ISAIRGAPPED: 'true'
XPACK_SECURITY_ENABLED: "true"
XPACK_SECURITY_ENROLLMENT_ENABLED: "false"
XPACK_SECURITY_HTTP_SSL_ENABLED: "false"
XPACK_FLEET_ISAIRGAPPED: "true"
I18N_LOCALE: zh-CN
SERVER_PORT: '5601'
SERVER_PORT: "5601"
ELASTICSEARCH_HOSTS: http://elasticsearch:9200
ports:
- ${KIBANA_PORT:-5601}:5601
healthcheck:
test: [ 'CMD-SHELL', 'curl -s http://localhost:5601 >/dev/null || exit 1' ]
test: ["CMD-SHELL", "curl -s http://localhost:5601 >/dev/null || exit 1"]
interval: 30s
timeout: 10s
retries: 3

+ 19
- 16
web/app/components/base/chat/chat-with-history/chat-wrapper.tsx Wyświetl plik

@@ -52,6 +52,10 @@ const ChatWrapper = () => {
allInputsHidden,
initUserVariables,
} = useChatWithHistoryContext()

// Semantic variable for better code readability
const isHistoryConversation = !!currentConversationId

const appConfig = useMemo(() => {
const config = appParams || {}

@@ -62,9 +66,9 @@ const ChatWrapper = () => {
fileUploadConfig: (config as any).system_parameters,
},
supportFeedback: true,
opening_statement: currentConversationId ? currentConversationItem?.introduction : (config as any).opening_statement,
opening_statement: isHistoryConversation ? currentConversationItem?.introduction : (config as any).opening_statement,
} as ChatConfig
}, [appParams, currentConversationItem?.introduction, currentConversationId])
}, [appParams, currentConversationItem?.introduction, isHistoryConversation])
const {
chatList,
setTargetMessageId,
@@ -75,7 +79,7 @@ const ChatWrapper = () => {
} = useChat(
appConfig,
{
inputs: (currentConversationId ? currentConversationInputs : newConversationInputs) as any,
inputs: (isHistoryConversation ? currentConversationInputs : newConversationInputs) as any,
inputsForm: inputsForms,
},
appPrevChatTree,
@@ -83,7 +87,7 @@ const ChatWrapper = () => {
clearChatList,
setClearChatList,
)
const inputsFormValue = currentConversationId ? currentConversationInputs : newConversationInputsRef?.current
const inputsFormValue = isHistoryConversation ? currentConversationInputs : newConversationInputsRef?.current
const inputDisabled = useMemo(() => {
if (allInputsHidden)
return false
@@ -132,7 +136,7 @@ const ChatWrapper = () => {
const data: any = {
query: message,
files,
inputs: formatBooleanInputs(inputsForms, currentConversationId ? currentConversationInputs : newConversationInputs),
inputs: formatBooleanInputs(inputsForms, isHistoryConversation ? currentConversationInputs : newConversationInputs),
conversation_id: currentConversationId,
parent_message_id: (isRegenerate ? parentAnswer?.id : getLastAnswer(chatList)?.id) || null,
}
@@ -142,11 +146,11 @@ const ChatWrapper = () => {
data,
{
onGetSuggestedQuestions: responseItemId => fetchSuggestedQuestions(responseItemId, isInstalledApp, appId),
onConversationComplete: currentConversationId ? undefined : handleNewConversationCompleted,
onConversationComplete: isHistoryConversation ? undefined : handleNewConversationCompleted,
isPublicAPI: !isInstalledApp,
},
)
}, [chatList, handleNewConversationCompleted, handleSend, currentConversationId, currentConversationInputs, newConversationInputs, isInstalledApp, appId])
}, [chatList, handleNewConversationCompleted, handleSend, isHistoryConversation, currentConversationInputs, newConversationInputs, isInstalledApp, appId])

const doRegenerate = useCallback((chatItem: ChatItemInTree, editedQuestion?: { message: string, files?: FileEntity[] }) => {
const question = editedQuestion ? chatItem : chatList.find(item => item.id === chatItem.parentMessageId)!
@@ -159,31 +163,30 @@ const ChatWrapper = () => {
}, [chatList, doSend])

const messageList = useMemo(() => {
if (currentConversationId)
return chatList
// Always filter out opening statement from message list as it's handled separately in welcome component
return chatList.filter(item => !item.isOpeningStatement)
}, [chatList, currentConversationId])
}, [chatList])

const [collapsed, setCollapsed] = useState(!!currentConversationId)
const [collapsed, setCollapsed] = useState(isHistoryConversation)

const chatNode = useMemo(() => {
if (allInputsHidden || !inputsForms.length)
return null
if (isMobile) {
if (!currentConversationId)
if (!isHistoryConversation)
return <InputsForm collapsed={collapsed} setCollapsed={setCollapsed} />
return null
}
else {
return <InputsForm collapsed={collapsed} setCollapsed={setCollapsed} />
}
}, [inputsForms.length, isMobile, currentConversationId, collapsed, allInputsHidden])
}, [inputsForms.length, isMobile, isHistoryConversation, collapsed, allInputsHidden])

const welcome = useMemo(() => {
const welcomeMessage = chatList.find(item => item.isOpeningStatement)
if (respondingState)
return null
if (currentConversationId)
if (isHistoryConversation)
return null
if (!welcomeMessage)
return null
@@ -224,7 +227,7 @@ const ChatWrapper = () => {
</div>
</div>
)
}, [appData?.site.icon, appData?.site.icon_background, appData?.site.icon_type, appData?.site.icon_url, chatList, collapsed, currentConversationId, inputsForms.length, respondingState, allInputsHidden])
}, [appData?.site.icon, appData?.site.icon_background, appData?.site.icon_type, appData?.site.icon_url, chatList, collapsed, isHistoryConversation, inputsForms.length, respondingState, allInputsHidden])

const answerIcon = (appData?.site && appData.site.use_icon_as_answer_icon)
? <AnswerIcon
@@ -248,7 +251,7 @@ const ChatWrapper = () => {
chatFooterClassName='pb-4'
chatFooterInnerClassName={`mx-auto w-full max-w-[768px] ${isMobile ? 'px-2' : 'px-4'}`}
onSend={doSend}
inputs={currentConversationId ? currentConversationInputs as any : newConversationInputs}
inputs={isHistoryConversation ? currentConversationInputs as any : newConversationInputs}
inputsForm={inputsForms}
onRegenerate={doRegenerate}
onStopResponding={handleStop}

+ 1
- 1
web/app/components/header/account-setting/model-provider-page/declarations.ts Wyświetl plik

@@ -14,7 +14,7 @@ export enum FormTypeEnum {
secretInput = 'secret-input',
select = 'select',
radio = 'radio',
boolean = 'boolean',
boolean = 'checkbox',
files = 'files',
file = 'file',
modelSelector = 'model-selector',

+ 2
- 2
web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx Wyświetl plik

@@ -115,7 +115,7 @@ const ModelModal: FC<ModelModalProps> = ({
const [selectedCredential, setSelectedCredential] = useState<Credential & { addNewCredential?: boolean } | undefined>()
const formRef2 = useRef<FormRefObject>(null)
const isEditMode = !!Object.keys(formValues).filter((key) => {
return key !== '__model_name' && key !== '__model_type'
return key !== '__model_name' && key !== '__model_type' && !!formValues[key]
}).length && isCurrentWorkspaceManager

const handleSave = useCallback(async () => {
@@ -167,7 +167,7 @@ const ModelModal: FC<ModelModalProps> = ({
__authorization_name__,
...rest
} = values
if (__model_name && __model_type && __authorization_name__) {
if (__model_name && __model_type) {
await handleSaveCredential({
credential_id: credential?.credential_id,
credentials: rest,

+ 4
- 4
web/app/components/workflow/nodes/_base/components/form-input-item.tsx Wyświetl plik

@@ -90,8 +90,8 @@ const FormInputItem: FC<Props> = ({
// return VarType.appSelector
// else if (isModelSelector)
// return VarType.modelSelector
// else if (isBoolean)
// return VarType.boolean
else if (isBoolean)
return VarType.boolean
else if (isObject)
return VarType.object
else if (isArray)
@@ -183,7 +183,7 @@ const FormInputItem: FC<Props> = ({
return (
<div className={cn('gap-1', !(isShowJSONEditor && isConstant) && 'flex')}>
{showTypeSwitch && (
<FormInputTypeSwitch value={varInput?.type || VarKindType.constant} onChange={handleTypeChange}/>
<FormInputTypeSwitch value={varInput?.type || VarKindType.constant} onChange={handleTypeChange} />
)}
{isString && (
<MixedVariableTextInput
@@ -203,7 +203,7 @@ const FormInputItem: FC<Props> = ({
placeholder={placeholder?.[language] || placeholder?.en_US}
/>
)}
{isBoolean && (
{isBoolean && isConstant && (
<FormInputBoolean
value={varInput?.value as boolean}
onChange={handleValueChange}

+ 1
- 0
web/i18n/de-DE/common.ts Wyświetl plik

@@ -490,6 +490,7 @@ const translation = {
providerManagedTip: 'Die aktuelle Konfiguration wird vom Anbieter gehostet.',
configLoadBalancing: 'Konfiguration Lastenverteilung',
specifyModelCredentialTip: 'Verwenden Sie ein konfiguriertes Modellzugang.',
manageCredentials: 'Anmeldeinformationen verwalten',
},
},
dataSource: {

+ 1
- 0
web/i18n/fa-IR/common.ts Wyświetl plik

@@ -494,6 +494,7 @@ const translation = {
specifyModelCredentialTip: 'از اعتبارنامه مدل پیکربندی شده استفاده کنید.',
providerManagedTip: 'تنظیمات فعلی توسط ارائه‌دهنده میزبانی می‌شود.',
modelCredentials: 'مدل اعتبارنامه',
manageCredentials: 'مدیریت اعتبارنامه ها',
},
},
dataSource: {

+ 1
- 0
web/i18n/hi-IN/common.ts Wyświetl plik

@@ -510,6 +510,7 @@ const translation = {
specifyModelCredential: 'मॉडल की क्रेडेंशियल निर्दिष्ट करें',
specifyModelCredentialTip: 'कॉन्फ़िगर की गई मॉडल क्रेडेंशियल का उपयोग करें।',
providerManagedTip: 'वर्तमान कॉन्फ़िगरेशन प्रदाता द्वारा होस्ट किया गया है।',
selectModelCredential: 'एक मॉडल क्रेडेंशियल चुनें',
},
},
dataSource: {

+ 10
- 10
web/i18n/id-ID/app-annotation.ts Wyświetl plik

@@ -5,10 +5,10 @@ const translation = {
},
table: {
header: {
answer: 'menjawab',
answer: 'Jawaban',
question: 'pertanyaan',
createdAt: 'dibuat di',
hits: 'Hits',
hits: 'Kecocokan',
addAnnotation: 'Tambahkan Anotasi',
bulkImport: 'Impor Massal',
clearAllConfirm: 'Menghapus semua anotasi?',
@@ -29,7 +29,7 @@ const translation = {
answerName: 'Bot Pendongeng',
},
addModal: {
answerName: 'Menjawab',
answerName: 'Jawaban',
title: 'Tambahkan Anotasi Balasan',
queryName: 'Pertanyaan',
createNext: 'Tambahkan respons beranotasi lainnya',
@@ -44,10 +44,10 @@ const translation = {
run: 'Jalankan Batch',
cancel: 'Membatalkan',
title: 'Impor Massal',
browse: 'ramban',
browse: 'Telusuri',
template: 'Unduh templat di sini',
tip: 'File CSV harus sesuai dengan struktur berikut:',
answer: 'menjawab',
answer: 'Jawaban',
contentTitle: 'konten potongan',
processing: 'Dalam pemrosesan batch',
completed: 'Impor selesai',
@@ -69,15 +69,15 @@ const translation = {
answerRequired: 'Jawaban diperlukan',
},
viewModal: {
hit: 'Pukul',
hitHistory: 'Riwayat Hit',
noHitHistory: 'Tidak ada riwayat hit',
hit: 'Kecocokan',
hitHistory: 'Riwayat Kecocokan',
noHitHistory: 'Tidak ada riwayat kecocokan',
annotatedResponse: 'Balas Anotasi',
hits: 'Hits',
hits: 'Kecocokan',
},
hitHistoryTable: {
response: 'Jawaban',
match: 'Korek api',
match: 'Kecocokan',
query: 'Kueri',
source: 'Sumber',
time: 'Waktu',

+ 13
- 13
web/i18n/id-ID/app-api.ts Wyświetl plik

@@ -19,8 +19,8 @@ const translation = {
completionMode: {
createCompletionApi: 'Membuat Pesan Penyelesaian',
messageIDTip: 'ID Pesan',
messageFeedbackApi: 'Umpan balik pesan (seperti)',
ratingTip: 'suka atau tidak suka, null adalah undo',
messageFeedbackApi: 'Umpan balik pesan (mis. spam, tidak relevan, pujian)',
ratingTip: '(mis. suka/tidak suka), null berarti membatalkan penilaian',
parametersApi: 'Dapatkan informasi parameter aplikasi',
parametersApiTip: 'Ambil parameter Input yang dikonfigurasi, termasuk nama variabel, nama bidang, jenis, dan nilai default. Biasanya digunakan untuk menampilkan bidang ini dalam formulir atau mengisi nilai default setelah klien dimuat.',
info: 'Untuk pembuatan teks berkualitas tinggi, seperti artikel, ringkasan, dan terjemahan, gunakan API pesan penyelesaian dengan input pengguna. Pembuatan teks bergantung pada parameter model dan templat prompt yang ditetapkan di Dify Prompt Engineering.',
@@ -48,7 +48,7 @@ const translation = {
conversationsListLimitTip: 'Berapa banyak obrolan yang dikembalikan dalam satu permintaan',
chatMsgHistoryLimit: 'Berapa banyak obrolan yang dikembalikan dalam satu permintaan',
conversationsListFirstIdTip: 'ID rekaman terakhir di halaman saat ini, default tidak ada.',
messageFeedbackApi: 'Umpan balik pengguna terminal pesan, seperti',
messageFeedbackApi: 'Umpan balik pengguna terminal pesan (mis. spam, tidak relevan, pujian)',
parametersApi: 'Dapatkan informasi parameter aplikasi',
streaming: 'streaming kembali. Implementasi pengembalian streaming berdasarkan SSE (Server-Sent Events).',
inputsTips: '(Opsional) Berikan bidang input pengguna sebagai pasangan kunci-nilai, sesuai dengan variabel di Prompt Eng. Kunci adalah nama variabel, Nilai adalah nilai parameter. Jika jenis bidang adalah Pilih, Nilai yang dikirimkan harus menjadi salah satu pilihan prasetel.',
@@ -58,7 +58,7 @@ const translation = {
createChatApiTip: 'Buat pesan percakapan baru atau lanjutkan dialog yang ada.',
chatMsgHistoryApiTip: 'Halaman pertama mengembalikan bilah \'batas\' terbaru, yang dalam urutan terbalik.',
conversationsListApi: 'Dapatkan daftar percakapan',
ratingTip: 'suka atau tidak suka, null adalah undo',
ratingTip: '(mis. suka/tidak suka), null berarti membatalkan penilaian',
conversationRenamingApi: 'Penggantian nama percakapan',
},
develop: {
@@ -67,19 +67,19 @@ const translation = {
pathParams: 'Parameter Jalur',
requestBody: 'Isi Permintaan',
},
apiServer: 'API Server',
apiServer: 'Server API',
copied: 'Disalin',
copy: 'Menyalin',
ok: 'Dalam Layanan',
regenerate: 'Regenerasi',
status: 'Keadaan',
copy: 'Salin',
ok: 'OK',
regenerate: 'Hasilkan Ulang',
status: 'Status',
never: 'Tidak pernah',
playing: 'Bermain',
play: 'Bermain',
disabled: 'Cacat',
playing: 'Sedang Memutar',
play: 'Putar',
disabled: 'Dinonaktifkan',
apiKey: 'Kunci API',
pause: 'Jeda',
loading: 'Loading',
loading: 'Memuat...',
}

export default translation

+ 4
- 4
web/i18n/id-ID/app-debug.ts Wyświetl plik

@@ -324,7 +324,7 @@ const translation = {
},
variableTable: {
action: 'Tindakan',
typeString: 'Tali',
typeString: 'String',
optional: 'Fakultatif',
typeSelect: 'Pilih',
type: 'Jenis Masukan',
@@ -346,7 +346,7 @@ const translation = {
name: 'Audio',
},
document: {
name: 'Surat',
name: 'Dokumen',
},
video: {
name: 'Video',
@@ -421,7 +421,7 @@ const translation = {
language: 'Bahasa',
title: 'Pengaturan Suara',
autoPlay: 'Putar Otomatis',
autoPlayDisabled: 'Off',
autoPlayDisabled: 'Dinonaktifkan',
resolutionTooltip: 'Bahasa pendukung suara text-to-speech。',
},
settings: 'Pengaturan',
@@ -448,7 +448,7 @@ const translation = {
},
inputs: {
queryPlaceholder: 'Silakan masukkan teks permintaan.',
run: 'LARI',
run: 'Jalankan',
completionVarTip: 'Isi nilai variabel, yang akan secara otomatis diganti dengan kata-kata prompt setiap kali pertanyaan diajukan.',
noVar: 'Isi nilai variabel, yang akan secara otomatis diganti dalam kata prompt setiap kali sesi baru dimulai.',
noPrompt: 'Coba tulis beberapa prompt dalam input pra-prompt',

+ 8
- 8
web/i18n/id-ID/app-log.ts Wyświetl plik

@@ -5,8 +5,8 @@ const translation = {
version: 'VERSI',
time: 'Waktu yang dibuat',
messageCount: 'Jumlah Pesan',
summary: 'Titel',
adminRate: 'Tingkat Op.',
summary: 'Ringkasan',
adminRate: 'Tingkat Admin',
user: 'Pengguna Akhir atau Akun',
startTime: 'WAKTU MULAI',
updatedTime: 'Waktu yang diperbarui',
@@ -18,8 +18,8 @@ const translation = {
runtime: 'WAKTU BERJALAN',
},
pagination: {
previous: 'Prev',
next: 'Depan',
previous: 'Sebelumnya',
next: 'Selanjutnya',
},
empty: {
element: {
@@ -30,12 +30,12 @@ const translation = {
},
},
detail: {
timeConsuming: '',
timeConsuming: 'Memakan waktu',
operation: {
dislike: 'tidak suka',
like: 'suka',
addAnnotation: 'Tambahkan Peningkatan',
editAnnotation: 'Edit Peningkatan',
addAnnotation: 'Tambahkan Anotasi',
editAnnotation: 'Edit Anotasi',
annotationPlaceholder: 'Masukkan jawaban yang diharapkan yang Anda inginkan untuk dibalas AI, yang dapat digunakan untuk penyempurnaan model dan peningkatan berkelanjutan kualitas pembuatan teks di masa mendatang.',
},
time: 'Waktu',
@@ -67,7 +67,7 @@ const translation = {
},
ascending: 'Naik',
descending: 'Turun',
sortBy: 'Kota hitam:',
sortBy: 'Urutkan berdasarkan',
},
runDetail: {
fileListDetail: 'Detail',

+ 2
- 2
web/i18n/id-ID/app-overview.ts Wyświetl plik

@@ -119,8 +119,8 @@ const translation = {
explanation: 'Mudah diintegrasikan ke dalam aplikasi Anda',
},
status: {
disable: 'Cacat',
running: 'Dalam Layanan',
disable: 'Nonaktif',
running: 'Berjalan',
},
title: 'Ikhtisar',
},

+ 8
- 8
web/i18n/id-ID/app.ts Wyświetl plik

@@ -23,7 +23,7 @@ const translation = {
appCreated: 'Aplikasi dibuat',
appNamePlaceholder: 'Beri nama aplikasi Anda',
appCreateDSLErrorPart3: 'Versi DSL aplikasi saat ini:',
Cancel: 'Membatalkan',
Cancel: 'Batal',
previewDemo: 'Pratinjau demo',
appCreateDSLWarning: 'Perhatian: Perbedaan versi DSL dapat memengaruhi fitur tertentu',
appCreateDSLErrorPart1: 'Perbedaan yang signifikan dalam versi DSL telah terdeteksi. Memaksa impor dapat menyebabkan aplikasi tidak berfungsi.',
@@ -46,7 +46,7 @@ const translation = {
showTemplates: 'Saya ingin memilih dari templat',
caution: 'Hati',
chatbotShortDescription: 'Chatbot berbasis LLM dengan pengaturan sederhana',
Confirm: 'Mengkonfirmasi',
Confirm: 'Konfirmasi',
agentAssistant: 'Asisten Agen Baru',
appCreateFailed: 'Gagal membuat aplikasi',
appCreateDSLErrorTitle: 'Ketidakcocokan Versi',
@@ -58,7 +58,7 @@ const translation = {
appTypeRequired: 'Silakan pilih jenis aplikasi',
advancedShortDescription: 'Alur kerja disempurnakan untuk obrolan multi-giliran',
completeAppIntro: 'Saya ingin membuat aplikasi yang menghasilkan teks berkualitas tinggi berdasarkan petunjuk, seperti menghasilkan artikel, ringkasan, terjemahan, dan banyak lagi.',
Create: 'Menciptakan',
Create: 'Buat',
advancedUserDescription: 'Alur kerja dengan fitur memori tambahan dan antarmuka chatbot.',
dropDSLToCreateApp: 'Jatuhkan file DSL di sini untuk membuat aplikasi',
completeApp: 'Pembuat Teks',
@@ -83,10 +83,10 @@ const translation = {
searchAllTemplate: 'Cari semua templat...',
},
iconPicker: {
cancel: 'Membatalkan',
cancel: 'Batal',
emoji: 'Emoji',
image: 'Citra',
ok: 'OKE',
ok: 'OK',
},
answerIcon: {
title: 'Gunakan ikon aplikasi web untuk mengganti 🤖',
@@ -129,7 +129,7 @@ const translation = {
},
weave: {
description: 'Weave adalah platform sumber terbuka untuk mengevaluasi, menguji, dan memantau aplikasi LLM.',
title: 'Anyam',
title: 'Weave',
},
aliyun: {
title: 'Monitor Awan',
@@ -148,8 +148,8 @@ const translation = {
collapse: 'Roboh',
tracing: 'Menelusuri',
title: 'Melacak performa aplikasi',
disabled: 'Cacat',
enabled: 'Dalam Layanan',
disabled: 'Nonaktif',
enabled: 'Aktif',
config: 'Konfigurasi',
description: 'Mengonfigurasi penyedia LLMOps Pihak Ketiga dan melacak performa aplikasi.',
inUse: 'Sedang digunakan',

+ 18
- 18
web/i18n/id-ID/common.ts Wyświetl plik

@@ -1,9 +1,9 @@
const translation = {
theme: {
theme: 'Tema',
light: 'ringan',
auto: 'sistem',
dark: 'gelap',
light: 'Terang',
auto: 'Otomatis',
dark: 'Gelap',
},
api: {
success: 'Keberhasilan',
@@ -16,8 +16,8 @@ const translation = {
setup: 'Setup',
download: 'Mengunduh',
getForFree: 'Dapatkan gratis',
reload: 'Reload',
lineBreak: 'Istirahat baris',
reload: 'Muat Ulang',
lineBreak: 'Baris Baru',
learnMore: 'Pelajari lebih lanjut',
saveAndRegenerate: 'Simpan & Buat Ulang Potongan Anak',
zoomOut: 'Perkecil',
@@ -26,7 +26,7 @@ const translation = {
selectAll: 'Pilih Semua',
in: 'di',
skip: 'Lewat',
remove: 'Buka',
remove: 'Hapus',
rename: 'Ubah nama',
close: 'Tutup',
ok: 'OKE',
@@ -35,8 +35,8 @@ const translation = {
log: 'Batang',
delete: 'Menghapus',
viewDetails: 'Lihat Detail',
view: 'Melihat',
clear: 'Jelas',
view: 'Lihat',
clear: 'Hapus',
deleteApp: 'Hapus Aplikasi',
downloadSuccess: 'Unduh Selesai.',
change: 'Ubah',
@@ -45,7 +45,7 @@ const translation = {
copied: 'Disalin',
deSelectAll: 'Batalkan pilihan Semua',
saveAndEnable: 'Simpan & Aktifkan',
refresh: 'Restart',
refresh: 'Segarkan',
downloadFailed: 'Unduhan gagal. Silakan coba lagi nanti.',
edit: 'Mengedit',
send: 'Kirim',
@@ -56,7 +56,7 @@ const translation = {
add: 'Tambah',
copy: 'Menyalin',
audioSourceUnavailable: 'AudioSource tidak tersedia',
submit: 'Tunduk',
submit: 'Kirim',
duplicate: 'Duplikat',
save: 'Simpan',
added: 'Ditambahkan',
@@ -100,7 +100,7 @@ const translation = {
},
},
unit: {
char: 'Tank',
char: 'karakter',
},
actionMsg: {
noModification: 'Tidak ada modifikasi saat ini.',
@@ -148,7 +148,7 @@ const translation = {
account: 'Rekening',
newApp: 'Aplikasi Baru',
explore: 'Menjelajahi',
apps: 'Belajar',
apps: 'Aplikasi',
status: 'beta',
tools: 'Perkakas',
exploreMarketplace: 'Jelajahi Marketplace',
@@ -165,7 +165,7 @@ const translation = {
settings: 'Pengaturan',
support: 'Dukung',
github: 'GitHub',
about: 'Sekitar',
about: 'Tentang',
workspace: 'Workspace',
createWorkspace: 'Membuat Ruang Kerja',
},
@@ -503,13 +503,13 @@ const translation = {
},
integratedAlert: 'Notion terintegrasi melalui kredensial internal, tidak perlu mengotorisasi ulang.',
disconnected: 'Terputus',
remove: 'Buka',
remove: 'Hapus',
addWorkspace: 'Menambahkan ruang kerja',
description: 'Menggunakan Notion sebagai sumber data untuk Pengetahuan.',
connected: 'Terhubung',
pagesAuthorized: 'Halaman yang disahkan',
changeAuthorizedPages: 'Mengubah halaman resmi',
title: 'Gagasan',
title: 'Notion',
sync: 'Sync',
connectedWorkspace: 'Ruang kerja yang terhubung',
},
@@ -597,7 +597,7 @@ const translation = {
'claude-2': 'Claude-2',
'gpt-3.5-turbo': 'GPT-3.5-Turbo',
'gpt-4': 'GPT-4',
'whisper-1': 'Bisikan-1',
'whisper-1': 'Whisper-1',
'text-davinci-003': 'Teks-Davinci-003',
'gpt-4-32k': 'GPT-4-32K',
'gpt-3.5-turbo-16k': 'GPT-3.5-Turbo-16K',
@@ -615,7 +615,7 @@ const translation = {
},
resend: 'Kirim Ulang',
conversationName: 'Nama percakapan',
thinking: 'Pikiran...',
thinking: 'Sedang berpikir...',
conversationNameCanNotEmpty: 'Nama percakapan diperlukan',
thought: 'Pikiran',
renameConversation: 'Ganti Nama Percakapan',
@@ -712,7 +712,7 @@ const translation = {
deleteDescription: 'Apakah Anda yakin ingin menghapus gambar profil Anda? Akun Anda akan menggunakan avatar awal default.',
},
imageInput: {
browse: 'ramban',
browse: 'Telusuri',
supportedFormats: 'Mendukung PNG, JPG, JPEG, WEBP dan GIF',
dropImageHere: 'Letakkan gambar Anda di sini, atau',
},

+ 9
- 9
web/i18n/id-ID/custom.ts Wyświetl plik

@@ -7,25 +7,25 @@ const translation = {
},
webapp: {
changeLogoTip: 'Format SVG atau PNG dengan ukuran minimum 40x40px',
removeBrand: 'Hapus Didukung oleh Dify',
changeLogo: 'Perubahan Didukung oleh Citra Merek',
title: 'Sesuaikan merek aplikasi web',
removeBrand: 'Hapus Branding Dify',
changeLogo: 'Ubah Logo Merek',
title: 'Kustomisasi Branding Aplikasi Web',
},
app: {
title: 'Menyesuaikan merek header aplikasi',
title: 'Kustomisasi Branding Header Aplikasi',
changeLogoTip: 'Format SVG atau PNG dengan ukuran minimal 80x80px',
},
customize: {
suffix: 'untuk meningkatkan ke edisi Enterprise.',
prefix: 'Untuk menyesuaikan logo merek di dalam aplikasi, silakan',
contactUs: 'Hubungi',
suffix: 'untuk upgrade ke edisi Enterprise.',
prefix: 'Untuk kustomisasi logo merek di dalam aplikasi, silakan',
contactUs: 'Hubungi Kami',
},
custom: 'Kustomisasi',
uploading: 'Meng',
uploading: 'Mengunggah...',
upload: 'Unggah',
change: 'Ubah',
restore: 'Pulihkan Default',
apply: 'Berlaku',
apply: 'Terapkan',
uploadedFail: 'Unggahan gambar gagal, silakan unggah ulang.',
}


+ 10
- 10
web/i18n/id-ID/dataset-creation.ts Wyświetl plik

@@ -56,17 +56,17 @@ const translation = {
tip: 'Pengetahuan kosong tidak akan berisi dokumen, dan Anda dapat mengunggah dokumen kapan saja.',
},
website: {
configure: 'Mengkonfigurasi',
configure: 'Konfigurasikan',
fireCrawlNotConfigured: 'Firecrawl tidak dikonfigurasi',
chooseProvider: 'Pilih penyedia',
configureFirecrawl: 'Mengonfigurasi Firecrawl',
configureFirecrawl: 'Konfigurasikan Firecrawl',
watercrawlDoc: 'Dokumen Watercrawl',
options: 'Pilihan',
firecrawlTitle: 'Mengekstrak konten web dengan 🔥Firecrawl',
jinaReaderNotConfigured: 'Jina Reader tidak dikonfigurasi',
preview: 'Pratayang',
resetAll: 'Atur Ulang Semua',
run: 'Lari',
run: 'Jalankan',
limit: 'Batas',
useSitemap: 'Menggunakan peta situs',
jinaReaderDoc: 'Pelajari lebih lanjut tentang Jina Reader',
@@ -87,19 +87,19 @@ const translation = {
maxDepth: 'Kedalaman maks',
jinaReaderDocLink: 'https://jina.ai/reader',
selectAll: 'Pilih Semua',
maxDepthTooltip: 'Kedalaman maksimum untuk di-crawl relatif terhadap URL yang dimasukkan. Kedalaman 0 hanya mengikis halaman url yang dimasukkan, kedalaman 1 mengikis url dan semuanya setelah dimasukkanURL satu /, dan seterusnya.',
maxDepthTooltip: 'Kedalaman maksimum untuk di-crawl relatif terhadap URL yang dimasukkan. Kedalaman 0 hanya mengikis halaman url yang dimasukkan, kedalaman 1 mengikis url dan semuanya setelah dimasukkan URL satu /, dan seterusnya.',
waterCrawlNotConfiguredDescription: 'Konfigurasikan Watercrawl dengan kunci API untuk menggunakannya.',
firecrawlDoc: 'Dokumen Firecrawl',
configureWatercrawl: 'Mengonfigurasi Watercrawl',
configureWatercrawl: 'Konfigurasikan Watercrawl',
},
pagePreview: 'Pratinjau Halaman',
notionSyncTitle: 'Gagasan tidak terhubung',
notionSyncTitle: 'Notion tidak terhubung',
filePreview: 'Pratinjau File',
cancel: 'Membatalkan',
emptyDatasetCreation: 'Saya ingin membuat Pengetahuan kosong',
button: 'Depan',
button: 'Berikutnya',
notionSyncTip: 'Untuk menyinkronkan dengan Notion, koneksi ke Notion harus dibuat terlebih dahulu.',
connect: 'Buka terhubung',
connect: 'Hubungkan',
},
stepTwo: {
paragraph: 'Paragraf',
@@ -162,7 +162,7 @@ const translation = {
previewChunkTip: 'Klik tombol \'Pratinjau Potongan\' di sebelah kiri untuk memuat pratinjau',
sideTipP4: 'Potongan dan pembersihan yang tepat meningkatkan kinerja model, memberikan hasil yang lebih akurat dan berharga.',
previewTitleButton: 'Pratayang',
switch: 'Sakelar',
switch: 'Beralih',
datasetSettingLink: 'Pengaturan pengetahuan.',
rules: 'Aturan Pra-pemrosesan Teks',
sideTipP2: 'Segmentasi membagi teks panjang menjadi paragraf sehingga model dapat memahami dengan lebih baik. Ini meningkatkan kualitas dan relevansi hasil model.',
@@ -193,7 +193,7 @@ const translation = {
resume: 'Melanjutkan pemrosesan',
stop: 'Hentikan pemrosesan',
creationContent: 'Kami secara otomatis menamai Pengetahuan, Anda dapat memodifikasinya kapan saja.',
modelButtonConfirm: 'Mengkonfirmasi',
modelButtonConfirm: 'Konfirmasi',
sideTipContent: 'Setelah dokumen selesai diindeks, Pengetahuan dapat diintegrasikan ke dalam aplikasi sebagai konteks, Anda dapat menemukan pengaturan konteks di halaman orkestrasi perintah. Anda juga dapat membuatnya sebagai plugin pengindeksan ChatGPT independen untuk dirilis.',
modelButtonCancel: 'Membatalkan',
label: 'Nama pengetahuan',

+ 2
- 2
web/i18n/id-ID/dataset-hit-testing.ts Wyświetl plik

@@ -11,7 +11,7 @@ const translation = {
countWarning: 'Hingga 200 karakter.',
placeholder: 'Silakan masukkan teks, disarankan untuk memasukkan kalimat deklaratif singkat.',
indexWarning: 'Pengetahuan berkualitas tinggi saja.',
testing: 'Ujian',
testing: 'Pengujian',
},
hit: {
emptyTip: 'Hasil Pengujian Pengambilan akan ditampilkan di sini',
@@ -22,7 +22,7 @@ const translation = {
open: 'Buka',
settingTitle: 'Pengaturan Pengambilan',
dateTimeFormat: 'MM / DD / YYYY hh: mm A',
desc: 'Uji efek pukulan Pengetahuan berdasarkan teks kueri yang diberikan.',
desc: 'Uji dampak pengetahuan terhadap hasil pencarian berdasarkan teks kueri yang diberikan.',
viewDetail: 'Lihat Detail',
viewChart: 'Lihat GRAFIK VAKTOR',
chunkDetail: 'Detail Potongan',

+ 1
- 1
web/i18n/id-ID/dataset-settings.ts Wyświetl plik

@@ -3,7 +3,7 @@ const translation = {
retrievalSetting: {
title: 'Pengaturan Pengambilan',
description: 'tentang metode pengambilan.',
longDescription: 'tentang metode pengambilan, Anda dapat mengudagnya kapan saja di pengaturan Pengetahuan.',
longDescription: 'tentang metode pengambilan, Anda dapat mengunduhnya kapan saja di pengaturan Pengetahuan.',
method: 'Metode Pengambilan',
learnMore: 'Pelajari lebih lanjut',
},

+ 2
- 2
web/i18n/id-ID/dataset.ts Wyświetl plik

@@ -133,7 +133,7 @@ const translation = {
search: 'Metadata pencarian',
},
datasetMetadata: {
disabled: 'Cacat',
disabled: 'Nonaktif',
addMetaData: 'Tambahkan Metadata',
description: 'Anda dapat mengelola semua metadata dalam pengetahuan ini di sini. Modifikasi akan disinkronkan ke setiap dokumen.',
deleteTitle: 'Konfirmasi untuk menghapus',
@@ -141,7 +141,7 @@ const translation = {
rename: 'Ubah nama',
builtInDescription: 'Metadata bawaan secara otomatis diekstrak dan dihasilkan. Itu harus diaktifkan sebelum digunakan dan tidak dapat diedit.',
namePlaceholder: 'Nama metadata',
builtIn: 'Built-in',
builtIn: 'Bawaan',
},
documentMetadata: {
metadataToolTip: 'Metadata berfungsi sebagai filter penting yang meningkatkan akurasi dan relevansi pengambilan informasi. Anda dapat memodifikasi dan menambahkan metadata untuk dokumen ini di sini.',

+ 1
- 1
web/i18n/id-ID/education.ts Wyświetl plik

@@ -60,7 +60,7 @@ const translation = {
},
dateFormat: 'MM / DD / YYYY',
},
submit: 'Tunduk',
submit: 'Kirim',
toVerified: 'Dapatkan Pendidikan Terverifikasi',
currentSigned: 'SAAT INI MASUK SEBAGAI',
successTitle: 'Anda telah mendapatkan Dify Education Verified',

+ 4
- 4
web/i18n/id-ID/explore.ts Wyświetl plik

@@ -1,10 +1,10 @@
const translation = {
sidebar: {
action: {
unpin: 'Lepaskan pin',
pin: 'Pin',
delete: 'Menghapus',
rename: 'Ubah nama',
unpin: 'Lepaskan sematan',
pin: 'Sematkan',
delete: 'Hapus',
rename: 'Ganti nama',
},
delete: {
content: 'Apakah Anda yakin ingin menghapus aplikasi ini?',

+ 13
- 13
web/i18n/id-ID/login.ts Wyświetl plik

@@ -36,8 +36,8 @@ const translation = {
continueWithCode: 'Lanjutkan dengan kode',
sendVerificationCode: 'Kirim Kode Verifikasi',
invalidInvitationCode: 'Kode undangan tidak valid',
installBtn: 'Mengatur',
joinTipStart: 'Mengundang Anda bergabung',
installBtn: 'Siapkan',
joinTipStart: 'Mengundang Anda untuk bergabung',
or: 'ATAU',
namePlaceholder: 'Nama pengguna Anda',
withSSO: 'Lanjutkan dengan SSO',
@@ -53,23 +53,23 @@ const translation = {
invitationCodePlaceholder: 'Kode undangan Anda',
emailPlaceholder: 'Email Anda',
tos: 'Ketentuan Layanan',
go: 'Pergi ke Dify',
go: 'Buka Dify',
forgotPassword: 'Lupa Kata Sandi Anda?',
sendUsMail: 'Kirimkan perkenalan Anda melalui email kepada kami, dan kami akan menangani permintaan undangan.',
pp: 'Kebijakan Privasi',
activatedTipEnd: 'tim',
backToSignIn: 'Kembali untuk login',
backToSignIn: 'Kembali ke halaman masuk',
passwordChanged: 'Masuk sekarang',
withGitHub: 'Lanjutkan dengan GitHub',
accountAlreadyInited: 'Akun sudah diinisialisasi',
withGoogle: 'Lanjutkan dengan Google',
rightDesc: 'Bangun aplikasi AI yang menawan secara visual, dapat dioperasikan, dan ditingkatkan dengan mudah.',
rightDesc: 'Bangun aplikasi AI yang menarik secara visual, mudah dioperasikan, dan mudah diskalakan.',
invitationCode: 'Kode Undangan',
invalidToken: 'Token tidak valid atau kedaluwarsa',
setAdminAccount: 'Menyiapkan akun admin',
forgotPasswordDesc: 'Silakan masukkan alamat email Anda untuk mengatur ulang kata sandi Anda. Kami akan mengirimi Anda email dengan instruksi tentang cara mengatur ulang kata sandi Anda.',
confirmPassword: 'Konfirmasi Kata Sandi',
changePasswordBtn: 'Menetapkan kata sandi',
changePasswordBtn: 'Tetapkan kata sandi',
resetPassword: 'Atur Ulang Kata Sandi',
explore: 'Jelajahi Dify',
useVerificationCode: 'Gunakan Kode Verifikasi',
@@ -84,7 +84,7 @@ const translation = {
licenseLost: 'Lisensi Hilang',
licenseInactive: 'Lisensi Tidak Aktif',
enterYourName: 'Silakan masukkan nama pengguna Anda',
back: 'Belakang',
back: 'Kembali',
activated: 'Masuk sekarang',
goToInit: 'Jika Anda belum menginisialisasi akun, silakan buka halaman inisialisasi',
licenseExpired: 'Lisensi Kedaluwarsa',
@@ -94,9 +94,9 @@ const translation = {
validate: 'Memvalidasi',
resetPasswordDesc: 'Ketik email yang Anda gunakan untuk mendaftar di Dify dan kami akan mengirimkan email reset kata sandi kepada Anda.',
licenseLostTip: 'Gagal menghubungkan server lisensi Dify. Hubungi administrator Anda untuk terus menggunakan Dify.',
signBtn: 'Tandatangan',
signBtn: 'Masuk',
sendResetLink: 'Kirim tautan reset',
createAndSignIn: 'Membuat dan masuk',
createAndSignIn: 'Buat dan masuk',
licenseExpiredTip: 'Lisensi Dify Enterprise untuk ruang kerja Anda telah kedaluwarsa. Hubungi administrator Anda untuk terus menggunakan Dify.',
email: 'Alamat email',
noLoginMethodTip: 'Silakan hubungi admin sistem untuk menambahkan metode autentikasi.',
@@ -104,11 +104,11 @@ const translation = {
licenseInactiveTip: 'Lisensi Dify Enterprise untuk ruang kerja Anda tidak aktif. Hubungi administrator Anda untuk terus menggunakan Dify.',
rightTitle: 'Buka potensi penuh LLM',
welcome: '👋 Selamat datang di Dify, silakan login untuk melanjutkan.',
changePassword: 'Menetapkan kata sandi',
setAdminAccountDesc: 'Hak istimewa maksimum untuk akun admin, yang dapat digunakan untuk membuat aplikasi dan mengelola penyedia LLM, dll.',
join: 'Ikat',
changePassword: 'Ubah kata sandi',
setAdminAccountDesc: 'Akun admin memiliki hak istimewa penuh untuk membuat aplikasi, mengelola penyedia LLM, dll.',
join: 'Gabung',
forget: 'Lupa Kata Sandi Anda?',
backToLogin: 'Kembali ke login',
backToLogin: 'Kembali ke halaman masuk',
oneMoreStep: 'Satu langkah lagi',
}


+ 5
- 5
web/i18n/id-ID/oauth.ts Wyświetl plik

@@ -17,11 +17,11 @@ const translation = {
authorizeFailed: 'Otorisasi gagal',
authAppInfoFetchFailed: 'Gagal mengambil info aplikasi untuk otorisasi',
},
continue: 'Terus',
unknownApp: 'Aplikasi Tidak Dikenal',
login: 'Login',
connect: 'Hubungkan ke',
switchAccount: 'Beralih Akun',
continue: 'Lanjut',
unknownApp: 'Aplikasi tidak dikenal',
login: 'Masuk',
connect: 'Hubungkan',
switchAccount: 'Ganti Akun',
}

export default translation

+ 5
- 5
web/i18n/id-ID/plugin.ts Wyświetl plik

@@ -39,7 +39,7 @@ const translation = {
local: 'Plugin Lokal',
},
operation: {
remove: 'Buka',
remove: 'Hapus',
info: 'Plugin Info',
update: 'Pemutakhiran',
detail: 'Rincian',
@@ -55,7 +55,7 @@ const translation = {
empty: 'Klik tombol \' \' untuk menambahkan alat. Anda dapat menambahkan beberapa alat.',
params: 'KONFIGURASI PENALARAN',
unsupportedMCPTool: 'Saat ini versi plugin strategi agen yang dipilih tidak mendukung alat MCP.',
auto: 'Mobil',
auto: 'Otomatis',
descriptionPlaceholder: 'Deskripsi singkat tentang tujuan alat, misalnya, mendapatkan suhu untuk lokasi tertentu.',
toolSetting: 'Pengaturan Alat',
settings: 'PENGATURAN PENGGUNA',
@@ -174,11 +174,11 @@ const translation = {
installing: 'Menginstal...',
uploadFailed: 'Upload gagal',
pluginLoadErrorDesc: 'Plugin ini tidak akan diinstal',
next: 'Depan',
next: 'Lanjut',
installedSuccessfully: 'Instalasi berhasil',
install: 'Pasang',
installFailed: 'Instalasi gagal',
back: 'Belakang',
back: 'Kembali',
readyToInstallPackage: 'Tentang menginstal plugin berikut',
installedSuccessfullyDesc: 'Plugin telah berhasil diinstal.',
pluginLoadError: 'Kesalahan pemuatan plugin',
@@ -227,7 +227,7 @@ const translation = {
empower: 'Berdayakan pengembangan AI Anda',
partnerTip: 'Diverifikasi oleh partner Dify',
moreFrom: 'Selengkapnya dari Marketplace',
sortBy: 'Kota hitam',
sortBy: 'Urutkan berdasarkan',
and: 'dan',
difyMarketplace: 'Dify Marketplace',
verifiedTip: 'Diverifikasi oleh Dify',

+ 8
- 8
web/i18n/id-ID/time.ts Wyświetl plik

@@ -1,17 +1,17 @@
const translation = {
daysInWeek: {
Wed: 'Bertaruh',
Wed: 'Rabu',
Thu: 'Kamis',
Sun: 'Matahari',
Tue: 'Membunuh',
Mon: 'Mon',
Sat: 'Hari sabtu',
Fri: 'Bebas',
Sun: 'Minggu',
Tue: 'Selasa',
Mon: 'Senin',
Sat: 'Sabtu',
Fri: 'Jumat',
},
months: {
August: 'Agustus',
October: 'Oktober',
May: 'Menjahit',
May: 'Mei',
September: 'September',
December: 'Desember',
November: 'November',
@@ -34,7 +34,7 @@ const translation = {
dateFormats: {
display: 'MMMM D, YYYY',
input: 'YYYY-MM-DD',
outputWithTime: 'YYYY-MM-DDTHH:mm:ss. SSSZ',
outputWithTime: 'YYYY-MM-DDTHH:mm:ss.SSSZ',
output: 'YYYY-MM-DD',
displayWithTime: 'MMMM D, YYYY hh:mm A',
},

+ 13
- 13
web/i18n/id-ID/workflow.ts Wyświetl plik

@@ -14,7 +14,7 @@ const translation = {
title: 'Tarik',
},
},
undo: 'Buka',
undo: 'Urungkan',
embedIntoSite: 'Sematkan ke Situs',
editing: 'Mengedit',
inRunMode: 'Dalam Mode Jalankan',
@@ -33,10 +33,10 @@ const translation = {
addParallelNode: 'Tambahkan Node Paralel',
onFailure: 'Pada Kegagalan',
update: 'Pemutakhiran',
parallelRun: 'Lari Paralel',
configure: 'Mengkonfigurasi',
parallelRun: 'Jalankan Paralel',
configure: 'Konfigurasikan',
copy: 'Menyalin',
redo: 'Siap',
redo: 'Ulangi',
runApp: 'Jalankan Aplikasi',
noHistory: 'Tidak Ada Sejarah',
importDSLTip: 'Draf saat ini akan ditimpa.\nEkspor alur kerja sebagai cadangan sebelum mengimpor.',
@@ -54,7 +54,7 @@ const translation = {
importSuccess: 'Berhasil Impor',
jumpToNode: 'Lompat ke simpul ini',
tagBound: 'Jumlah aplikasi yang menggunakan tag ini',
model: 'Pola',
model: 'Model',
workflowAsToolTip: 'Konfigurasi ulang alat diperlukan setelah pembaruan alur kerja.',
currentDraft: 'Draf Saat Ini',
parallel: 'SEJAJAR',
@@ -65,7 +65,7 @@ const translation = {
importWarning: 'Hati',
running: 'Menjalankan',
publishedAt: 'Diterbitkan',
run: 'Lari',
run: 'Jalankan',
importDSL: 'Impor DSL',
featuresDescription: 'Tingkatkan pengalaman pengguna aplikasi web',
inPreviewMode: 'Dalam Mode Pratinjau',
@@ -168,9 +168,9 @@ const translation = {
nodeAdd: 'Node ditambahkan',
nodePaste: 'Node ditempelkan',
noteDelete: 'Catatan dihapus',
hint: 'Indian',
hint: 'Petunjuk',
nodeTitleChange: 'Judul simpul diubah',
title: 'Perubahan Riwayat',
title: 'Riwayat Perubahan',
nodeDescriptionChange: 'Deskripsi simpul diubah',
clearHistory: 'Hapus Sejarah',
placeholder: 'Anda belum mengubah apa pun',
@@ -189,7 +189,7 @@ const translation = {
errorMsg: {
fields: {
variable: 'Nama Variabel',
model: 'Pola',
model: 'Model',
rerankModel: 'Model Peringkat Ulang yang dikonfigurasi',
visionVariable: 'Variabel Penglihatan',
variableValue: 'Nilai Variabel',
@@ -238,11 +238,11 @@ const translation = {
'question-classifier': 'Pengklasifikasi Pertanyaan',
'iteration-start': 'Iterasi Mulai',
'knowledge-retrieval': 'Pengambilan Pengetahuan',
'loop': 'Lari',
'loop': 'Perulangan',
'assigner': 'Penerima Variabel',
'agent': 'Agen',
'list-operator': 'Operator Daftar',
'answer': 'Menjawab',
'answer': 'Jawaban',
'parameter-extractor': 'Ekstraktor Parameter',
'document-extractor': 'Ekstraktor Dokumen',
'end': 'Ujung',
@@ -286,10 +286,10 @@ const translation = {
horizontal: 'Horisontal',
distributeHorizontal: 'Spasi Secara Horizontal',
zoomTo100: 'Perbesar hingga 100%',
alignLeft: 'Kiri',
alignLeft: 'Rata Kiri',
distributeVertical: 'Ruang Secara Vertikal',
zoomTo50: 'Perbesar hingga 50%',
alignBottom: 'Dasar',
alignBottom: 'Rata Bawah',
},
variableReference: {
conversationVars: 'variabel percakapan',

+ 1
- 0
web/i18n/ro-RO/common.ts Wyświetl plik

@@ -490,6 +490,7 @@ const translation = {
providerManagedTip: 'Configurarea curentă este găzduită de furnizor.',
modelCredentials: 'Credențiale model',
specifyModelCredentialTip: 'Utilizați un acreditiv de model configurat.',
addNewModelCredential: 'Adăugați acreditive noi pentru model',
},
},
dataSource: {

+ 1
- 0
web/i18n/th-TH/common.ts Wyświetl plik

@@ -489,6 +489,7 @@ const translation = {
authorizationError: 'ข้อผิดพลาดในการอนุญาต',
specifyModelCredentialTip: 'ใช้ข้อมูลรับรองโมเดลที่กำหนดไว้',
providerManagedTip: 'การกำหนดค่าปัจจุบันถูกโฮสต์โดยผู้ให้บริการ.',
customModelCredentialsDeleteTip: 'ข้อมูลรับรองกำลังถูกใช้งานและไม่สามารถลบได้',
},
},
dataSource: {

+ 1
- 1
web/i18n/tr-TR/common.ts Wyświetl plik

@@ -746,7 +746,7 @@ const translation = {
content: 'Geri Bildirim İçeriği',
subtitle: 'Lütfen bu yanıtla ilgili neyin yanlış gittiğini bize bildirin',
title: 'Geri Bildirim Sağla',
placeholder: 'Lütfen neyin yanlış gittiğini veya nasıl iyileşebileceğimizı açıklayın...',
placeholder: 'Lütfen neyin yanlış gittiğini veya nasıl iyileşebileceğimizi açıklayın...',
},
}


+ 1
- 1
web/i18n/uk-UA/common.ts Wyświetl plik

@@ -747,7 +747,7 @@ const translation = {
title: 'Надати відгук',
content: 'Зміст відгуку',
placeholder: 'Будь ласка, опишіть, що пішло не так або як ми можемо покращити...',
subtitle: 'Будь ласка, скажіть нам, що пішло не так з цим відповіді',
subtitle: 'Будь ласка, скажіть нам, що пішло не так із цією відповіддю',
},
}


+ 2
- 2
web/package.json Wyświetl plik

@@ -1,8 +1,8 @@
{
"name": "dify-web",
"version": "1.8.0",
"version": "1.8.1",
"private": true,
"packageManager": "pnpm@10.15.0",
"packageManager": "pnpm@10.15.1",
"engines": {
"node": ">=v22.11.0"
},

Ładowanie…
Anuluj
Zapisz