Browse Source

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

tags/2.0.0-beta.1
-LAN- 1 month ago
parent
commit
9c96b23d55
No account linked to committer's email address
46 changed files with 435 additions and 274 deletions
  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 View File

prepare-api: prepare-api:
@echo "🔧 Setting up API environment..." @echo "🔧 Setting up API environment..."
@cp -n api/.env.example api/.env 2>/dev/null || echo "API .env already exists" @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 @cd api && uv run flask db upgrade
@echo "✅ API environment prepared (not started)" @echo "✅ API environment prepared (not started)"



+ 1
- 0
api/.env.example View File

DB_HOST=localhost DB_HOST=localhost
DB_PORT=5432 DB_PORT=5432
DB_DATABASE=dify DB_DATABASE=dify
SQLALCHEMY_POOL_PRE_PING=true


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

+ 22
- 7
api/controllers/console/auth/oauth_server.py View File

from typing import cast from typing import cast


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




authorization_header = request.headers.get("Authorization") authorization_header = request.headers.get("Authorization")
if not authorization_header: 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: 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() token_type = parts[0].strip()
if token_type.lower() != "bearer": 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() access_token = parts[1].strip()
if not access_token: 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) account = OAuthServerService.validate_oauth_access_token(oauth_provider_app.client_id, access_token)
if not account: 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 kwargs["account"] = account



+ 1
- 1
api/core/app/apps/advanced_chat/generate_task_pipeline.py View File

err = self._base_task_pipeline._handle_error(event=event, session=session, message_id=self._message_id) 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) 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.""" """Handle workflow started events."""
with self._database_session() as session: with self._database_session() as session:
workflow_execution = self._workflow_cycle_manager.handle_workflow_run_start() workflow_execution = self._workflow_cycle_manager.handle_workflow_run_start()

+ 8
- 8
api/core/entities/provider_configuration.py View File

:return: :return:
""" """
with Session(db.engine) as session: 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: else:
credential_name = self._generate_provider_credential_name(session) credential_name = self._generate_provider_credential_name(session)


:return: :return:
""" """
with Session(db.engine) as session: 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: else:
credential_name = self._generate_custom_model_credential_name( credential_name = self._generate_custom_model_credential_name(
model=model, model_type=model_type, session=session model=model, model_type=model_type, session=session

+ 13
- 10
api/core/helper/position_helper.py View File

import os import os
from collections import OrderedDict from collections import OrderedDict
from collections.abc import Callable from collections.abc import Callable
from typing import Any
from typing import TypeVar


from configs import dify_config from configs import dify_config
from core.tools.utils.yaml_utils import load_yaml_file from core.tools.utils.yaml_utils import load_yaml_file
return position_map return position_map




T = TypeVar("T")


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


def sort_by_position_map( def sort_by_position_map(
position_map: dict[str, int], 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. 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. If the name of the object is not in the position map, it will be put at the end.


def sort_to_dict_by_position_map( def sort_to_dict_by_position_map(
position_map: dict[str, int], 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. 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. If the name of the object is not in the position map, it will be put at the end.
:return: an OrderedDict with the sorted pairs of name and object :return: an OrderedDict with the sorted pairs of name and object
""" """
sorted_items = sort_by_position_map(position_map, data, name_func) 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 View File

logger.debug("Received 202 Accepted") logger.debug("Received 202 Accepted")
return return


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

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

+ 1
- 1
api/core/mcp/server/streamable_http.py View File

parameters[item.variable]["type"] = "string" parameters[item.variable]["type"] = "string"
parameters[item.variable]["enum"] = item.options parameters[item.variable]["enum"] = item.options
elif item.type == VariableEntityType.NUMBER: elif item.type == VariableEntityType.NUMBER:
parameters[item.variable]["type"] = "float"
parameters[item.variable]["type"] = "number"
return parameters, required return parameters, required

+ 2
- 2
api/core/rag/datasource/vdb/myscale/myscale_vector.py View File

) )
for r in self._client.query(sql).named_results() 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 [] return []


def delete(self) -> None: def delete(self) -> None:

+ 3
- 2
api/extensions/storage/clickzetta_volume/file_lifecycle.py View File

"""ClickZetta Volume file lifecycle management """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 import json

+ 1
- 1
api/pyproject.toml View File

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


dependencies = [ dependencies = [

+ 64
- 1
api/tests/unit_tests/core/mcp/server/test_streamable_http.py View File

import json import json
from unittest.mock import Mock, patch from unittest.mock import Mock, patch


import jsonschema
import pytest import pytest


from core.app.app_config.entities import VariableEntity, VariableEntityType from core.app.app_config.entities import VariableEntity, VariableEntityType
assert parameters["category"]["enum"] == ["A", "B", "C"] assert parameters["category"]["enum"] == ["A", "B", "C"]


assert "count" in parameters 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 # FILE type should be skipped - it creates empty dict but gets filtered later
# Check that it doesn't have any meaningful content # Check that it doesn't have any meaningful content
assert "category" not in required assert "category" not in required


# Note: _get_request_id function has been removed as request_id is now passed as parameter # 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 View File



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

+ 71
- 38
docker/docker-compose-template.yaml View File

services: services:
# API service # API service
api: api:
image: langgenius/dify-api:1.8.0
image: langgenius/dify-api:1.8.1
restart: always restart: always
environment: environment:
# Use the shared environment variables. # Use the shared environment variables.
# worker service # worker service
# The Celery worker for processing the queue. # The Celery worker for processing the queue.
worker: worker:
image: langgenius/dify-api:1.8.0
image: langgenius/dify-api:1.8.1
restart: always restart: always
environment: environment:
# Use the shared environment variables. # Use the shared environment variables.
# worker_beat service # worker_beat service
# Celery beat for scheduling periodic tasks. # Celery beat for scheduling periodic tasks.
worker_beat: worker_beat:
image: langgenius/dify-api:1.8.0
image: langgenius/dify-api:1.8.1
restart: always restart: always
environment: environment:
# Use the shared environment variables. # Use the shared environment variables.


# Frontend web application. # Frontend web application.
web: web:
image: langgenius/dify-web:1.8.0
image: langgenius/dify-web:1.8.1
restart: always restart: always
environment: environment:
CONSOLE_API_URL: ${CONSOLE_API_URL:-} CONSOLE_API_URL: ${CONSOLE_API_URL:-}
volumes: volumes:
- ./volumes/db/data:/var/lib/postgresql/data - ./volumes/db/data:/var/lib/postgresql/data
healthcheck: 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 interval: 1s
timeout: 3s timeout: 3s
retries: 60 retries: 60
# Set the redis password when startup redis server. # Set the redis password when startup redis server.
command: redis-server --requirepass ${REDIS_PASSWORD:-difyai123456} command: redis-server --requirepass ${REDIS_PASSWORD:-difyai123456}
healthcheck: 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 # The DifySandbox
sandbox: sandbox:
- ./volumes/sandbox/dependencies:/dependencies - ./volumes/sandbox/dependencies:/dependencies
- ./volumes/sandbox/conf:/conf - ./volumes/sandbox/conf:/conf
healthcheck: healthcheck:
test: [ 'CMD', 'curl', '-f', 'http://localhost:8194/health' ]
test: ["CMD", "curl", "-f", "http://localhost:8194/health"]
networks: networks:
- ssrf_proxy_network - ssrf_proxy_network


volumes: volumes:
- ./ssrf_proxy/squid.conf.template:/etc/squid/squid.conf.template - ./ssrf_proxy/squid.conf.template:/etc/squid/squid.conf.template
- ./ssrf_proxy/docker-entrypoint.sh:/docker-entrypoint-mount.sh - ./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: environment:
# pls clearly modify the squid env vars to fit your network environment. # pls clearly modify the squid env vars to fit your network environment.
HTTP_PORT: ${SSRF_HTTP_PORT:-3128} HTTP_PORT: ${SSRF_HTTP_PORT:-3128}
- CERTBOT_EMAIL=${CERTBOT_EMAIL} - CERTBOT_EMAIL=${CERTBOT_EMAIL}
- CERTBOT_DOMAIN=${CERTBOT_DOMAIN} - CERTBOT_DOMAIN=${CERTBOT_DOMAIN}
- CERTBOT_OPTIONS=${CERTBOT_OPTIONS:-} - 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. # The nginx reverse proxy.
# used for reverse proxying the API service and Web service. # used for reverse proxying the API service and Web service.
- ./volumes/certbot/conf/live:/etc/letsencrypt/live # cert dir (with certbot container) - ./volumes/certbot/conf/live:/etc/letsencrypt/live # cert dir (with certbot container)
- ./volumes/certbot/conf:/etc/letsencrypt - ./volumes/certbot/conf:/etc/letsencrypt
- ./volumes/certbot/www:/var/www/html - ./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: environment:
NGINX_SERVER_NAME: ${NGINX_SERVER_NAME:-_} NGINX_SERVER_NAME: ${NGINX_SERVER_NAME:-_}
NGINX_HTTPS_ENABLED: ${NGINX_HTTPS_ENABLED:-false} NGINX_HTTPS_ENABLED: ${NGINX_HTTPS_ENABLED:-false}
- api - api
- web - web
ports: 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. # The Weaviate vector store.
weaviate: weaviate:
image: semitechnologies/weaviate:1.19.0 image: semitechnologies/weaviate:1.19.0
profiles: profiles:
- ''
- ""
- weaviate - weaviate
restart: always restart: always
volumes: volumes:
working_dir: /opt/couchbase working_dir: /opt/couchbase
stdin_open: true stdin_open: true
tty: true tty: true
entrypoint: [ "" ]
entrypoint: [""]
command: sh -c "/opt/couchbase/init/init-cbserver.sh" command: sh -c "/opt/couchbase/init/init-cbserver.sh"
volumes: volumes:
- ./volumes/couchbase/data:/opt/couchbase/var/lib/couchbase/data - ./volumes/couchbase/data:/opt/couchbase/var/lib/couchbase/data
healthcheck: healthcheck:
# ensure bucket was created before proceeding # 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 interval: 10s
retries: 10 retries: 10
start_period: 30s start_period: 30s
volumes: volumes:
- ./volumes/pgvector/data:/var/lib/postgresql/data - ./volumes/pgvector/data:/var/lib/postgresql/data
- ./pgvector/docker-entrypoint.sh:/docker-entrypoint.sh - ./pgvector/docker-entrypoint.sh:/docker-entrypoint.sh
entrypoint: [ '/docker-entrypoint.sh' ]
entrypoint: ["/docker-entrypoint.sh"]
healthcheck: healthcheck:
test: [ 'CMD', 'pg_isready' ]
test: ["CMD", "pg_isready"]
interval: 1s interval: 1s
timeout: 3s timeout: 3s
retries: 30 retries: 30
- VB_USERNAME=dify - VB_USERNAME=dify
- VB_PASSWORD=Difyai123456 - VB_PASSWORD=Difyai123456
ports: ports:
- '5434:5432'
- "5434:5432"
volumes: volumes:
- ./vastbase/lic:/home/vastbase/vastbase/lic - ./vastbase/lic:/home/vastbase/vastbase/lic
- ./vastbase/data:/home/vastbase/data - ./vastbase/data:/home/vastbase/data
- ./vastbase/backup:/home/vastbase/backup - ./vastbase/backup:/home/vastbase/backup
- ./vastbase/backup_log:/home/vastbase/backup_log - ./vastbase/backup_log:/home/vastbase/backup_log
healthcheck: healthcheck:
test: [ 'CMD', 'pg_isready' ]
test: ["CMD", "pg_isready"]
interval: 1s interval: 1s
timeout: 3s timeout: 3s
retries: 30 retries: 30
volumes: volumes:
- ./volumes/pgvecto_rs/data:/var/lib/postgresql/data - ./volumes/pgvecto_rs/data:/var/lib/postgresql/data
healthcheck: healthcheck:
test: [ 'CMD', 'pg_isready' ]
test: ["CMD", "pg_isready"]
interval: 1s interval: 1s
timeout: 3s timeout: 3s
retries: 30 retries: 30
ports: ports:
- "${OCEANBASE_VECTOR_PORT:-2881}:2881" - "${OCEANBASE_VECTOR_PORT:-2881}:2881"
healthcheck: 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 interval: 10s
retries: 30 retries: 30
start_period: 30s start_period: 30s
- ./volumes/milvus/etcd:/etcd - ./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 command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
healthcheck: healthcheck:
test: [ 'CMD', 'etcdctl', 'endpoint', 'health' ]
test: ["CMD", "etcdctl", "endpoint", "health"]
interval: 30s interval: 30s
timeout: 20s timeout: 20s
retries: 3 retries: 3
- ./volumes/milvus/minio:/minio_data - ./volumes/milvus/minio:/minio_data
command: minio server /minio_data --console-address ":9001" command: minio server /minio_data --console-address ":9001"
healthcheck: healthcheck:
test: [ 'CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live' ]
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s interval: 30s
timeout: 20s timeout: 20s
retries: 3 retries: 3
image: milvusdb/milvus:v2.5.15 image: milvusdb/milvus:v2.5.15
profiles: profiles:
- milvus - milvus
command: [ 'milvus', 'run', 'standalone' ]
command: ["milvus", "run", "standalone"]
environment: environment:
ETCD_ENDPOINTS: ${ETCD_ENDPOINTS:-etcd:2379} ETCD_ENDPOINTS: ${ETCD_ENDPOINTS:-etcd:2379}
MINIO_ADDRESS: ${MINIO_ADDRESS:-minio:9000} MINIO_ADDRESS: ${MINIO_ADDRESS:-minio:9000}
volumes: volumes:
- ./volumes/milvus/milvus:/var/lib/milvus - ./volumes/milvus/milvus:/var/lib/milvus
healthcheck: healthcheck:
test: [ 'CMD', 'curl', '-f', 'http://localhost:9091/healthz' ]
test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"]
interval: 30s interval: 30s
start_period: 90s start_period: 90s
timeout: 20s timeout: 20s
volumes: volumes:
- ./volumes/opengauss/data:/var/lib/opengauss/data - ./volumes/opengauss/data:/var/lib/opengauss/data
healthcheck: 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 interval: 10s
timeout: 10s timeout: 10s
retries: 10 retries: 10
node.name: dify-es0 node.name: dify-es0
discovery.type: single-node discovery.type: single-node
xpack.license.self_generated.type: basic 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: ports:
- ${ELASTICSEARCH_PORT:-9200}:9200 - ${ELASTICSEARCH_PORT:-9200}:9200
deploy: deploy:
resources: resources:
limits: limits:
memory: 2g memory: 2g
entrypoint: [ 'sh', '-c', "sh /docker-entrypoint-mount.sh" ]
entrypoint: ["sh", "-c", "sh /docker-entrypoint-mount.sh"]
healthcheck: healthcheck:
test: [ 'CMD', 'curl', '-s', 'http://localhost:9200/_cluster/health?pretty' ]
test:
["CMD", "curl", "-s", "http://localhost:9200/_cluster/health?pretty"]
interval: 30s interval: 30s
timeout: 10s timeout: 10s
retries: 50 retries: 50
environment: environment:
XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: d1a66dfd-c4d3-4a0a-8290-2abcb83ab3aa XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: d1a66dfd-c4d3-4a0a-8290-2abcb83ab3aa
NO_PROXY: localhost,127.0.0.1,elasticsearch,kibana 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 I18N_LOCALE: zh-CN
SERVER_PORT: '5601'
SERVER_PORT: "5601"
ELASTICSEARCH_HOSTS: http://elasticsearch:9200 ELASTICSEARCH_HOSTS: http://elasticsearch:9200
ports: ports:
- ${KIBANA_PORT:-5601}:5601 - ${KIBANA_PORT:-5601}:5601
healthcheck: 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 interval: 30s
timeout: 10s timeout: 10s
retries: 3 retries: 3

+ 71
- 38
docker/docker-compose.yaml View File

services: services:
# API service # API service
api: api:
image: langgenius/dify-api:1.8.0
image: langgenius/dify-api:1.8.1
restart: always restart: always
environment: environment:
# Use the shared environment variables. # Use the shared environment variables.
# worker service # worker service
# The Celery worker for processing the queue. # The Celery worker for processing the queue.
worker: worker:
image: langgenius/dify-api:1.8.0
image: langgenius/dify-api:1.8.1
restart: always restart: always
environment: environment:
# Use the shared environment variables. # Use the shared environment variables.
# worker_beat service # worker_beat service
# Celery beat for scheduling periodic tasks. # Celery beat for scheduling periodic tasks.
worker_beat: worker_beat:
image: langgenius/dify-api:1.8.0
image: langgenius/dify-api:1.8.1
restart: always restart: always
environment: environment:
# Use the shared environment variables. # Use the shared environment variables.


# Frontend web application. # Frontend web application.
web: web:
image: langgenius/dify-web:1.8.0
image: langgenius/dify-web:1.8.1
restart: always restart: always
environment: environment:
CONSOLE_API_URL: ${CONSOLE_API_URL:-} CONSOLE_API_URL: ${CONSOLE_API_URL:-}
volumes: volumes:
- ./volumes/db/data:/var/lib/postgresql/data - ./volumes/db/data:/var/lib/postgresql/data
healthcheck: 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 interval: 1s
timeout: 3s timeout: 3s
retries: 60 retries: 60
# Set the redis password when startup redis server. # Set the redis password when startup redis server.
command: redis-server --requirepass ${REDIS_PASSWORD:-difyai123456} command: redis-server --requirepass ${REDIS_PASSWORD:-difyai123456}
healthcheck: 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 # The DifySandbox
sandbox: sandbox:
- ./volumes/sandbox/dependencies:/dependencies - ./volumes/sandbox/dependencies:/dependencies
- ./volumes/sandbox/conf:/conf - ./volumes/sandbox/conf:/conf
healthcheck: healthcheck:
test: [ 'CMD', 'curl', '-f', 'http://localhost:8194/health' ]
test: ["CMD", "curl", "-f", "http://localhost:8194/health"]
networks: networks:
- ssrf_proxy_network - ssrf_proxy_network


volumes: volumes:
- ./ssrf_proxy/squid.conf.template:/etc/squid/squid.conf.template - ./ssrf_proxy/squid.conf.template:/etc/squid/squid.conf.template
- ./ssrf_proxy/docker-entrypoint.sh:/docker-entrypoint-mount.sh - ./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: environment:
# pls clearly modify the squid env vars to fit your network environment. # pls clearly modify the squid env vars to fit your network environment.
HTTP_PORT: ${SSRF_HTTP_PORT:-3128} HTTP_PORT: ${SSRF_HTTP_PORT:-3128}
- CERTBOT_EMAIL=${CERTBOT_EMAIL} - CERTBOT_EMAIL=${CERTBOT_EMAIL}
- CERTBOT_DOMAIN=${CERTBOT_DOMAIN} - CERTBOT_DOMAIN=${CERTBOT_DOMAIN}
- CERTBOT_OPTIONS=${CERTBOT_OPTIONS:-} - 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. # The nginx reverse proxy.
# used for reverse proxying the API service and Web service. # used for reverse proxying the API service and Web service.
- ./volumes/certbot/conf/live:/etc/letsencrypt/live # cert dir (with certbot container) - ./volumes/certbot/conf/live:/etc/letsencrypt/live # cert dir (with certbot container)
- ./volumes/certbot/conf:/etc/letsencrypt - ./volumes/certbot/conf:/etc/letsencrypt
- ./volumes/certbot/www:/var/www/html - ./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: environment:
NGINX_SERVER_NAME: ${NGINX_SERVER_NAME:-_} NGINX_SERVER_NAME: ${NGINX_SERVER_NAME:-_}
NGINX_HTTPS_ENABLED: ${NGINX_HTTPS_ENABLED:-false} NGINX_HTTPS_ENABLED: ${NGINX_HTTPS_ENABLED:-false}
- api - api
- web - web
ports: 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. # The Weaviate vector store.
weaviate: weaviate:
image: semitechnologies/weaviate:1.19.0 image: semitechnologies/weaviate:1.19.0
profiles: profiles:
- ''
- ""
- weaviate - weaviate
restart: always restart: always
volumes: volumes:
working_dir: /opt/couchbase working_dir: /opt/couchbase
stdin_open: true stdin_open: true
tty: true tty: true
entrypoint: [ "" ]
entrypoint: [""]
command: sh -c "/opt/couchbase/init/init-cbserver.sh" command: sh -c "/opt/couchbase/init/init-cbserver.sh"
volumes: volumes:
- ./volumes/couchbase/data:/opt/couchbase/var/lib/couchbase/data - ./volumes/couchbase/data:/opt/couchbase/var/lib/couchbase/data
healthcheck: healthcheck:
# ensure bucket was created before proceeding # 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 interval: 10s
retries: 10 retries: 10
start_period: 30s start_period: 30s
volumes: volumes:
- ./volumes/pgvector/data:/var/lib/postgresql/data - ./volumes/pgvector/data:/var/lib/postgresql/data
- ./pgvector/docker-entrypoint.sh:/docker-entrypoint.sh - ./pgvector/docker-entrypoint.sh:/docker-entrypoint.sh
entrypoint: [ '/docker-entrypoint.sh' ]
entrypoint: ["/docker-entrypoint.sh"]
healthcheck: healthcheck:
test: [ 'CMD', 'pg_isready' ]
test: ["CMD", "pg_isready"]
interval: 1s interval: 1s
timeout: 3s timeout: 3s
retries: 30 retries: 30
- VB_USERNAME=dify - VB_USERNAME=dify
- VB_PASSWORD=Difyai123456 - VB_PASSWORD=Difyai123456
ports: ports:
- '5434:5432'
- "5434:5432"
volumes: volumes:
- ./vastbase/lic:/home/vastbase/vastbase/lic - ./vastbase/lic:/home/vastbase/vastbase/lic
- ./vastbase/data:/home/vastbase/data - ./vastbase/data:/home/vastbase/data
- ./vastbase/backup:/home/vastbase/backup - ./vastbase/backup:/home/vastbase/backup
- ./vastbase/backup_log:/home/vastbase/backup_log - ./vastbase/backup_log:/home/vastbase/backup_log
healthcheck: healthcheck:
test: [ 'CMD', 'pg_isready' ]
test: ["CMD", "pg_isready"]
interval: 1s interval: 1s
timeout: 3s timeout: 3s
retries: 30 retries: 30
volumes: volumes:
- ./volumes/pgvecto_rs/data:/var/lib/postgresql/data - ./volumes/pgvecto_rs/data:/var/lib/postgresql/data
healthcheck: healthcheck:
test: [ 'CMD', 'pg_isready' ]
test: ["CMD", "pg_isready"]
interval: 1s interval: 1s
timeout: 3s timeout: 3s
retries: 30 retries: 30
ports: ports:
- "${OCEANBASE_VECTOR_PORT:-2881}:2881" - "${OCEANBASE_VECTOR_PORT:-2881}:2881"
healthcheck: 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 interval: 10s
retries: 30 retries: 30
start_period: 30s start_period: 30s
- ./volumes/milvus/etcd:/etcd - ./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 command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
healthcheck: healthcheck:
test: [ 'CMD', 'etcdctl', 'endpoint', 'health' ]
test: ["CMD", "etcdctl", "endpoint", "health"]
interval: 30s interval: 30s
timeout: 20s timeout: 20s
retries: 3 retries: 3
- ./volumes/milvus/minio:/minio_data - ./volumes/milvus/minio:/minio_data
command: minio server /minio_data --console-address ":9001" command: minio server /minio_data --console-address ":9001"
healthcheck: healthcheck:
test: [ 'CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live' ]
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s interval: 30s
timeout: 20s timeout: 20s
retries: 3 retries: 3
image: milvusdb/milvus:v2.5.15 image: milvusdb/milvus:v2.5.15
profiles: profiles:
- milvus - milvus
command: [ 'milvus', 'run', 'standalone' ]
command: ["milvus", "run", "standalone"]
environment: environment:
ETCD_ENDPOINTS: ${ETCD_ENDPOINTS:-etcd:2379} ETCD_ENDPOINTS: ${ETCD_ENDPOINTS:-etcd:2379}
MINIO_ADDRESS: ${MINIO_ADDRESS:-minio:9000} MINIO_ADDRESS: ${MINIO_ADDRESS:-minio:9000}
volumes: volumes:
- ./volumes/milvus/milvus:/var/lib/milvus - ./volumes/milvus/milvus:/var/lib/milvus
healthcheck: healthcheck:
test: [ 'CMD', 'curl', '-f', 'http://localhost:9091/healthz' ]
test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"]
interval: 30s interval: 30s
start_period: 90s start_period: 90s
timeout: 20s timeout: 20s
volumes: volumes:
- ./volumes/opengauss/data:/var/lib/opengauss/data - ./volumes/opengauss/data:/var/lib/opengauss/data
healthcheck: 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 interval: 10s
timeout: 10s timeout: 10s
retries: 10 retries: 10
node.name: dify-es0 node.name: dify-es0
discovery.type: single-node discovery.type: single-node
xpack.license.self_generated.type: basic 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: ports:
- ${ELASTICSEARCH_PORT:-9200}:9200 - ${ELASTICSEARCH_PORT:-9200}:9200
deploy: deploy:
resources: resources:
limits: limits:
memory: 2g memory: 2g
entrypoint: [ 'sh', '-c', "sh /docker-entrypoint-mount.sh" ]
entrypoint: ["sh", "-c", "sh /docker-entrypoint-mount.sh"]
healthcheck: healthcheck:
test: [ 'CMD', 'curl', '-s', 'http://localhost:9200/_cluster/health?pretty' ]
test:
["CMD", "curl", "-s", "http://localhost:9200/_cluster/health?pretty"]
interval: 30s interval: 30s
timeout: 10s timeout: 10s
retries: 50 retries: 50
environment: environment:
XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: d1a66dfd-c4d3-4a0a-8290-2abcb83ab3aa XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: d1a66dfd-c4d3-4a0a-8290-2abcb83ab3aa
NO_PROXY: localhost,127.0.0.1,elasticsearch,kibana 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 I18N_LOCALE: zh-CN
SERVER_PORT: '5601'
SERVER_PORT: "5601"
ELASTICSEARCH_HOSTS: http://elasticsearch:9200 ELASTICSEARCH_HOSTS: http://elasticsearch:9200
ports: ports:
- ${KIBANA_PORT:-5601}:5601 - ${KIBANA_PORT:-5601}:5601
healthcheck: 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 interval: 30s
timeout: 10s timeout: 10s
retries: 3 retries: 3

+ 19
- 16
web/app/components/base/chat/chat-with-history/chat-wrapper.tsx View File

allInputsHidden, allInputsHidden,
initUserVariables, initUserVariables,
} = useChatWithHistoryContext() } = useChatWithHistoryContext()

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

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


fileUploadConfig: (config as any).system_parameters, fileUploadConfig: (config as any).system_parameters,
}, },
supportFeedback: true, supportFeedback: true,
opening_statement: currentConversationId ? currentConversationItem?.introduction : (config as any).opening_statement,
opening_statement: isHistoryConversation ? currentConversationItem?.introduction : (config as any).opening_statement,
} as ChatConfig } as ChatConfig
}, [appParams, currentConversationItem?.introduction, currentConversationId])
}, [appParams, currentConversationItem?.introduction, isHistoryConversation])
const { const {
chatList, chatList,
setTargetMessageId, setTargetMessageId,
} = useChat( } = useChat(
appConfig, appConfig,
{ {
inputs: (currentConversationId ? currentConversationInputs : newConversationInputs) as any,
inputs: (isHistoryConversation ? currentConversationInputs : newConversationInputs) as any,
inputsForm: inputsForms, inputsForm: inputsForms,
}, },
appPrevChatTree, appPrevChatTree,
clearChatList, clearChatList,
setClearChatList, setClearChatList,
) )
const inputsFormValue = currentConversationId ? currentConversationInputs : newConversationInputsRef?.current
const inputsFormValue = isHistoryConversation ? currentConversationInputs : newConversationInputsRef?.current
const inputDisabled = useMemo(() => { const inputDisabled = useMemo(() => {
if (allInputsHidden) if (allInputsHidden)
return false return false
const data: any = { const data: any = {
query: message, query: message,
files, files,
inputs: formatBooleanInputs(inputsForms, currentConversationId ? currentConversationInputs : newConversationInputs),
inputs: formatBooleanInputs(inputsForms, isHistoryConversation ? currentConversationInputs : newConversationInputs),
conversation_id: currentConversationId, conversation_id: currentConversationId,
parent_message_id: (isRegenerate ? parentAnswer?.id : getLastAnswer(chatList)?.id) || null, parent_message_id: (isRegenerate ? parentAnswer?.id : getLastAnswer(chatList)?.id) || null,
} }
data, data,
{ {
onGetSuggestedQuestions: responseItemId => fetchSuggestedQuestions(responseItemId, isInstalledApp, appId), onGetSuggestedQuestions: responseItemId => fetchSuggestedQuestions(responseItemId, isInstalledApp, appId),
onConversationComplete: currentConversationId ? undefined : handleNewConversationCompleted,
onConversationComplete: isHistoryConversation ? undefined : handleNewConversationCompleted,
isPublicAPI: !isInstalledApp, 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 doRegenerate = useCallback((chatItem: ChatItemInTree, editedQuestion?: { message: string, files?: FileEntity[] }) => {
const question = editedQuestion ? chatItem : chatList.find(item => item.id === chatItem.parentMessageId)! const question = editedQuestion ? chatItem : chatList.find(item => item.id === chatItem.parentMessageId)!
}, [chatList, doSend]) }, [chatList, doSend])


const messageList = useMemo(() => { 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) return chatList.filter(item => !item.isOpeningStatement)
}, [chatList, currentConversationId])
}, [chatList])


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


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


const welcome = useMemo(() => { const welcome = useMemo(() => {
const welcomeMessage = chatList.find(item => item.isOpeningStatement) const welcomeMessage = chatList.find(item => item.isOpeningStatement)
if (respondingState) if (respondingState)
return null return null
if (currentConversationId)
if (isHistoryConversation)
return null return null
if (!welcomeMessage) if (!welcomeMessage)
return null return null
</div> </div>
</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) const answerIcon = (appData?.site && appData.site.use_icon_as_answer_icon)
? <AnswerIcon ? <AnswerIcon
chatFooterClassName='pb-4' chatFooterClassName='pb-4'
chatFooterInnerClassName={`mx-auto w-full max-w-[768px] ${isMobile ? 'px-2' : 'px-4'}`} chatFooterInnerClassName={`mx-auto w-full max-w-[768px] ${isMobile ? 'px-2' : 'px-4'}`}
onSend={doSend} onSend={doSend}
inputs={currentConversationId ? currentConversationInputs as any : newConversationInputs}
inputs={isHistoryConversation ? currentConversationInputs as any : newConversationInputs}
inputsForm={inputsForms} inputsForm={inputsForms}
onRegenerate={doRegenerate} onRegenerate={doRegenerate}
onStopResponding={handleStop} onStopResponding={handleStop}

+ 1
- 1
web/app/components/header/account-setting/model-provider-page/declarations.ts View File

secretInput = 'secret-input', secretInput = 'secret-input',
select = 'select', select = 'select',
radio = 'radio', radio = 'radio',
boolean = 'boolean',
boolean = 'checkbox',
files = 'files', files = 'files',
file = 'file', file = 'file',
modelSelector = 'model-selector', modelSelector = 'model-selector',

+ 2
- 2
web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx View File

const [selectedCredential, setSelectedCredential] = useState<Credential & { addNewCredential?: boolean } | undefined>() const [selectedCredential, setSelectedCredential] = useState<Credential & { addNewCredential?: boolean } | undefined>()
const formRef2 = useRef<FormRefObject>(null) const formRef2 = useRef<FormRefObject>(null)
const isEditMode = !!Object.keys(formValues).filter((key) => { 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 }).length && isCurrentWorkspaceManager


const handleSave = useCallback(async () => { const handleSave = useCallback(async () => {
__authorization_name__, __authorization_name__,
...rest ...rest
} = values } = values
if (__model_name && __model_type && __authorization_name__) {
if (__model_name && __model_type) {
await handleSaveCredential({ await handleSaveCredential({
credential_id: credential?.credential_id, credential_id: credential?.credential_id,
credentials: rest, credentials: rest,

+ 4
- 4
web/app/components/workflow/nodes/_base/components/form-input-item.tsx View File

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

+ 1
- 0
web/i18n/de-DE/common.ts View File

providerManagedTip: 'Die aktuelle Konfiguration wird vom Anbieter gehostet.', providerManagedTip: 'Die aktuelle Konfiguration wird vom Anbieter gehostet.',
configLoadBalancing: 'Konfiguration Lastenverteilung', configLoadBalancing: 'Konfiguration Lastenverteilung',
specifyModelCredentialTip: 'Verwenden Sie ein konfiguriertes Modellzugang.', specifyModelCredentialTip: 'Verwenden Sie ein konfiguriertes Modellzugang.',
manageCredentials: 'Anmeldeinformationen verwalten',
}, },
}, },
dataSource: { dataSource: {

+ 1
- 0
web/i18n/fa-IR/common.ts View File

specifyModelCredentialTip: 'از اعتبارنامه مدل پیکربندی شده استفاده کنید.', specifyModelCredentialTip: 'از اعتبارنامه مدل پیکربندی شده استفاده کنید.',
providerManagedTip: 'تنظیمات فعلی توسط ارائه‌دهنده میزبانی می‌شود.', providerManagedTip: 'تنظیمات فعلی توسط ارائه‌دهنده میزبانی می‌شود.',
modelCredentials: 'مدل اعتبارنامه', modelCredentials: 'مدل اعتبارنامه',
manageCredentials: 'مدیریت اعتبارنامه ها',
}, },
}, },
dataSource: { dataSource: {

+ 1
- 0
web/i18n/hi-IN/common.ts View File

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

+ 10
- 10
web/i18n/id-ID/app-annotation.ts View File

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

+ 13
- 13
web/i18n/id-ID/app-api.ts View File

completionMode: { completionMode: {
createCompletionApi: 'Membuat Pesan Penyelesaian', createCompletionApi: 'Membuat Pesan Penyelesaian',
messageIDTip: 'ID Pesan', 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', 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.', 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.', 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.',
conversationsListLimitTip: 'Berapa banyak obrolan yang dikembalikan dalam satu permintaan', conversationsListLimitTip: 'Berapa banyak obrolan yang dikembalikan dalam satu permintaan',
chatMsgHistoryLimit: '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.', 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', parametersApi: 'Dapatkan informasi parameter aplikasi',
streaming: 'streaming kembali. Implementasi pengembalian streaming berdasarkan SSE (Server-Sent Events).', 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.', 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.',
createChatApiTip: 'Buat pesan percakapan baru atau lanjutkan dialog yang ada.', createChatApiTip: 'Buat pesan percakapan baru atau lanjutkan dialog yang ada.',
chatMsgHistoryApiTip: 'Halaman pertama mengembalikan bilah \'batas\' terbaru, yang dalam urutan terbalik.', chatMsgHistoryApiTip: 'Halaman pertama mengembalikan bilah \'batas\' terbaru, yang dalam urutan terbalik.',
conversationsListApi: 'Dapatkan daftar percakapan', 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', conversationRenamingApi: 'Penggantian nama percakapan',
}, },
develop: { develop: {
pathParams: 'Parameter Jalur', pathParams: 'Parameter Jalur',
requestBody: 'Isi Permintaan', requestBody: 'Isi Permintaan',
}, },
apiServer: 'API Server',
apiServer: 'Server API',
copied: 'Disalin', copied: 'Disalin',
copy: 'Menyalin',
ok: 'Dalam Layanan',
regenerate: 'Regenerasi',
status: 'Keadaan',
copy: 'Salin',
ok: 'OK',
regenerate: 'Hasilkan Ulang',
status: 'Status',
never: 'Tidak pernah', never: 'Tidak pernah',
playing: 'Bermain',
play: 'Bermain',
disabled: 'Cacat',
playing: 'Sedang Memutar',
play: 'Putar',
disabled: 'Dinonaktifkan',
apiKey: 'Kunci API', apiKey: 'Kunci API',
pause: 'Jeda', pause: 'Jeda',
loading: 'Loading',
loading: 'Memuat...',
} }


export default translation export default translation

+ 4
- 4
web/i18n/id-ID/app-debug.ts View File

}, },
variableTable: { variableTable: {
action: 'Tindakan', action: 'Tindakan',
typeString: 'Tali',
typeString: 'String',
optional: 'Fakultatif', optional: 'Fakultatif',
typeSelect: 'Pilih', typeSelect: 'Pilih',
type: 'Jenis Masukan', type: 'Jenis Masukan',
name: 'Audio', name: 'Audio',
}, },
document: { document: {
name: 'Surat',
name: 'Dokumen',
}, },
video: { video: {
name: 'Video', name: 'Video',
language: 'Bahasa', language: 'Bahasa',
title: 'Pengaturan Suara', title: 'Pengaturan Suara',
autoPlay: 'Putar Otomatis', autoPlay: 'Putar Otomatis',
autoPlayDisabled: 'Off',
autoPlayDisabled: 'Dinonaktifkan',
resolutionTooltip: 'Bahasa pendukung suara text-to-speech。', resolutionTooltip: 'Bahasa pendukung suara text-to-speech。',
}, },
settings: 'Pengaturan', settings: 'Pengaturan',
}, },
inputs: { inputs: {
queryPlaceholder: 'Silakan masukkan teks permintaan.', 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.', 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.', 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', noPrompt: 'Coba tulis beberapa prompt dalam input pra-prompt',

+ 8
- 8
web/i18n/id-ID/app-log.ts View File

version: 'VERSI', version: 'VERSI',
time: 'Waktu yang dibuat', time: 'Waktu yang dibuat',
messageCount: 'Jumlah Pesan', messageCount: 'Jumlah Pesan',
summary: 'Titel',
adminRate: 'Tingkat Op.',
summary: 'Ringkasan',
adminRate: 'Tingkat Admin',
user: 'Pengguna Akhir atau Akun', user: 'Pengguna Akhir atau Akun',
startTime: 'WAKTU MULAI', startTime: 'WAKTU MULAI',
updatedTime: 'Waktu yang diperbarui', updatedTime: 'Waktu yang diperbarui',
runtime: 'WAKTU BERJALAN', runtime: 'WAKTU BERJALAN',
}, },
pagination: { pagination: {
previous: 'Prev',
next: 'Depan',
previous: 'Sebelumnya',
next: 'Selanjutnya',
}, },
empty: { empty: {
element: { element: {
}, },
}, },
detail: { detail: {
timeConsuming: '',
timeConsuming: 'Memakan waktu',
operation: { operation: {
dislike: 'tidak suka', dislike: 'tidak suka',
like: '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.', 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', time: 'Waktu',
}, },
ascending: 'Naik', ascending: 'Naik',
descending: 'Turun', descending: 'Turun',
sortBy: 'Kota hitam:',
sortBy: 'Urutkan berdasarkan',
}, },
runDetail: { runDetail: {
fileListDetail: 'Detail', fileListDetail: 'Detail',

+ 2
- 2
web/i18n/id-ID/app-overview.ts View File

explanation: 'Mudah diintegrasikan ke dalam aplikasi Anda', explanation: 'Mudah diintegrasikan ke dalam aplikasi Anda',
}, },
status: { status: {
disable: 'Cacat',
running: 'Dalam Layanan',
disable: 'Nonaktif',
running: 'Berjalan',
}, },
title: 'Ikhtisar', title: 'Ikhtisar',
}, },

+ 8
- 8
web/i18n/id-ID/app.ts View File

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

+ 18
- 18
web/i18n/id-ID/common.ts View File

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

+ 9
- 9
web/i18n/id-ID/custom.ts View File

}, },
webapp: { webapp: {
changeLogoTip: 'Format SVG atau PNG dengan ukuran minimum 40x40px', 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: { app: {
title: 'Menyesuaikan merek header aplikasi',
title: 'Kustomisasi Branding Header Aplikasi',
changeLogoTip: 'Format SVG atau PNG dengan ukuran minimal 80x80px', changeLogoTip: 'Format SVG atau PNG dengan ukuran minimal 80x80px',
}, },
customize: { 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', custom: 'Kustomisasi',
uploading: 'Meng',
uploading: 'Mengunggah...',
upload: 'Unggah', upload: 'Unggah',
change: 'Ubah', change: 'Ubah',
restore: 'Pulihkan Default', restore: 'Pulihkan Default',
apply: 'Berlaku',
apply: 'Terapkan',
uploadedFail: 'Unggahan gambar gagal, silakan unggah ulang.', uploadedFail: 'Unggahan gambar gagal, silakan unggah ulang.',
} }



+ 10
- 10
web/i18n/id-ID/dataset-creation.ts View File

tip: 'Pengetahuan kosong tidak akan berisi dokumen, dan Anda dapat mengunggah dokumen kapan saja.', tip: 'Pengetahuan kosong tidak akan berisi dokumen, dan Anda dapat mengunggah dokumen kapan saja.',
}, },
website: { website: {
configure: 'Mengkonfigurasi',
configure: 'Konfigurasikan',
fireCrawlNotConfigured: 'Firecrawl tidak dikonfigurasi', fireCrawlNotConfigured: 'Firecrawl tidak dikonfigurasi',
chooseProvider: 'Pilih penyedia', chooseProvider: 'Pilih penyedia',
configureFirecrawl: 'Mengonfigurasi Firecrawl',
configureFirecrawl: 'Konfigurasikan Firecrawl',
watercrawlDoc: 'Dokumen Watercrawl', watercrawlDoc: 'Dokumen Watercrawl',
options: 'Pilihan', options: 'Pilihan',
firecrawlTitle: 'Mengekstrak konten web dengan 🔥Firecrawl', firecrawlTitle: 'Mengekstrak konten web dengan 🔥Firecrawl',
jinaReaderNotConfigured: 'Jina Reader tidak dikonfigurasi', jinaReaderNotConfigured: 'Jina Reader tidak dikonfigurasi',
preview: 'Pratayang', preview: 'Pratayang',
resetAll: 'Atur Ulang Semua', resetAll: 'Atur Ulang Semua',
run: 'Lari',
run: 'Jalankan',
limit: 'Batas', limit: 'Batas',
useSitemap: 'Menggunakan peta situs', useSitemap: 'Menggunakan peta situs',
jinaReaderDoc: 'Pelajari lebih lanjut tentang Jina Reader', jinaReaderDoc: 'Pelajari lebih lanjut tentang Jina Reader',
maxDepth: 'Kedalaman maks', maxDepth: 'Kedalaman maks',
jinaReaderDocLink: 'https://jina.ai/reader', jinaReaderDocLink: 'https://jina.ai/reader',
selectAll: 'Pilih Semua', 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.', waterCrawlNotConfiguredDescription: 'Konfigurasikan Watercrawl dengan kunci API untuk menggunakannya.',
firecrawlDoc: 'Dokumen Firecrawl', firecrawlDoc: 'Dokumen Firecrawl',
configureWatercrawl: 'Mengonfigurasi Watercrawl',
configureWatercrawl: 'Konfigurasikan Watercrawl',
}, },
pagePreview: 'Pratinjau Halaman', pagePreview: 'Pratinjau Halaman',
notionSyncTitle: 'Gagasan tidak terhubung',
notionSyncTitle: 'Notion tidak terhubung',
filePreview: 'Pratinjau File', filePreview: 'Pratinjau File',
cancel: 'Membatalkan', cancel: 'Membatalkan',
emptyDatasetCreation: 'Saya ingin membuat Pengetahuan kosong', emptyDatasetCreation: 'Saya ingin membuat Pengetahuan kosong',
button: 'Depan',
button: 'Berikutnya',
notionSyncTip: 'Untuk menyinkronkan dengan Notion, koneksi ke Notion harus dibuat terlebih dahulu.', notionSyncTip: 'Untuk menyinkronkan dengan Notion, koneksi ke Notion harus dibuat terlebih dahulu.',
connect: 'Buka terhubung',
connect: 'Hubungkan',
}, },
stepTwo: { stepTwo: {
paragraph: 'Paragraf', paragraph: 'Paragraf',
previewChunkTip: 'Klik tombol \'Pratinjau Potongan\' di sebelah kiri untuk memuat pratinjau', 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.', sideTipP4: 'Potongan dan pembersihan yang tepat meningkatkan kinerja model, memberikan hasil yang lebih akurat dan berharga.',
previewTitleButton: 'Pratayang', previewTitleButton: 'Pratayang',
switch: 'Sakelar',
switch: 'Beralih',
datasetSettingLink: 'Pengaturan pengetahuan.', datasetSettingLink: 'Pengaturan pengetahuan.',
rules: 'Aturan Pra-pemrosesan Teks', 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.', sideTipP2: 'Segmentasi membagi teks panjang menjadi paragraf sehingga model dapat memahami dengan lebih baik. Ini meningkatkan kualitas dan relevansi hasil model.',
resume: 'Melanjutkan pemrosesan', resume: 'Melanjutkan pemrosesan',
stop: 'Hentikan pemrosesan', stop: 'Hentikan pemrosesan',
creationContent: 'Kami secara otomatis menamai Pengetahuan, Anda dapat memodifikasinya kapan saja.', 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.', 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', modelButtonCancel: 'Membatalkan',
label: 'Nama pengetahuan', label: 'Nama pengetahuan',

+ 2
- 2
web/i18n/id-ID/dataset-hit-testing.ts View File

countWarning: 'Hingga 200 karakter.', countWarning: 'Hingga 200 karakter.',
placeholder: 'Silakan masukkan teks, disarankan untuk memasukkan kalimat deklaratif singkat.', placeholder: 'Silakan masukkan teks, disarankan untuk memasukkan kalimat deklaratif singkat.',
indexWarning: 'Pengetahuan berkualitas tinggi saja.', indexWarning: 'Pengetahuan berkualitas tinggi saja.',
testing: 'Ujian',
testing: 'Pengujian',
}, },
hit: { hit: {
emptyTip: 'Hasil Pengujian Pengambilan akan ditampilkan di sini', emptyTip: 'Hasil Pengujian Pengambilan akan ditampilkan di sini',
open: 'Buka', open: 'Buka',
settingTitle: 'Pengaturan Pengambilan', settingTitle: 'Pengaturan Pengambilan',
dateTimeFormat: 'MM / DD / YYYY hh: mm A', 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', viewDetail: 'Lihat Detail',
viewChart: 'Lihat GRAFIK VAKTOR', viewChart: 'Lihat GRAFIK VAKTOR',
chunkDetail: 'Detail Potongan', chunkDetail: 'Detail Potongan',

+ 1
- 1
web/i18n/id-ID/dataset-settings.ts View File

retrievalSetting: { retrievalSetting: {
title: 'Pengaturan Pengambilan', title: 'Pengaturan Pengambilan',
description: 'tentang metode 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', method: 'Metode Pengambilan',
learnMore: 'Pelajari lebih lanjut', learnMore: 'Pelajari lebih lanjut',
}, },

+ 2
- 2
web/i18n/id-ID/dataset.ts View File

search: 'Metadata pencarian', search: 'Metadata pencarian',
}, },
datasetMetadata: { datasetMetadata: {
disabled: 'Cacat',
disabled: 'Nonaktif',
addMetaData: 'Tambahkan Metadata', addMetaData: 'Tambahkan Metadata',
description: 'Anda dapat mengelola semua metadata dalam pengetahuan ini di sini. Modifikasi akan disinkronkan ke setiap dokumen.', description: 'Anda dapat mengelola semua metadata dalam pengetahuan ini di sini. Modifikasi akan disinkronkan ke setiap dokumen.',
deleteTitle: 'Konfirmasi untuk menghapus', deleteTitle: 'Konfirmasi untuk menghapus',
rename: 'Ubah nama', rename: 'Ubah nama',
builtInDescription: 'Metadata bawaan secara otomatis diekstrak dan dihasilkan. Itu harus diaktifkan sebelum digunakan dan tidak dapat diedit.', builtInDescription: 'Metadata bawaan secara otomatis diekstrak dan dihasilkan. Itu harus diaktifkan sebelum digunakan dan tidak dapat diedit.',
namePlaceholder: 'Nama metadata', namePlaceholder: 'Nama metadata',
builtIn: 'Built-in',
builtIn: 'Bawaan',
}, },
documentMetadata: { 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.', 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 View File

}, },
dateFormat: 'MM / DD / YYYY', dateFormat: 'MM / DD / YYYY',
}, },
submit: 'Tunduk',
submit: 'Kirim',
toVerified: 'Dapatkan Pendidikan Terverifikasi', toVerified: 'Dapatkan Pendidikan Terverifikasi',
currentSigned: 'SAAT INI MASUK SEBAGAI', currentSigned: 'SAAT INI MASUK SEBAGAI',
successTitle: 'Anda telah mendapatkan Dify Education Verified', successTitle: 'Anda telah mendapatkan Dify Education Verified',

+ 4
- 4
web/i18n/id-ID/explore.ts View File

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

+ 13
- 13
web/i18n/id-ID/login.ts View File

continueWithCode: 'Lanjutkan dengan kode', continueWithCode: 'Lanjutkan dengan kode',
sendVerificationCode: 'Kirim Kode Verifikasi', sendVerificationCode: 'Kirim Kode Verifikasi',
invalidInvitationCode: 'Kode undangan tidak valid', invalidInvitationCode: 'Kode undangan tidak valid',
installBtn: 'Mengatur',
joinTipStart: 'Mengundang Anda bergabung',
installBtn: 'Siapkan',
joinTipStart: 'Mengundang Anda untuk bergabung',
or: 'ATAU', or: 'ATAU',
namePlaceholder: 'Nama pengguna Anda', namePlaceholder: 'Nama pengguna Anda',
withSSO: 'Lanjutkan dengan SSO', withSSO: 'Lanjutkan dengan SSO',
invitationCodePlaceholder: 'Kode undangan Anda', invitationCodePlaceholder: 'Kode undangan Anda',
emailPlaceholder: 'Email Anda', emailPlaceholder: 'Email Anda',
tos: 'Ketentuan Layanan', tos: 'Ketentuan Layanan',
go: 'Pergi ke Dify',
go: 'Buka Dify',
forgotPassword: 'Lupa Kata Sandi Anda?', forgotPassword: 'Lupa Kata Sandi Anda?',
sendUsMail: 'Kirimkan perkenalan Anda melalui email kepada kami, dan kami akan menangani permintaan undangan.', sendUsMail: 'Kirimkan perkenalan Anda melalui email kepada kami, dan kami akan menangani permintaan undangan.',
pp: 'Kebijakan Privasi', pp: 'Kebijakan Privasi',
activatedTipEnd: 'tim', activatedTipEnd: 'tim',
backToSignIn: 'Kembali untuk login',
backToSignIn: 'Kembali ke halaman masuk',
passwordChanged: 'Masuk sekarang', passwordChanged: 'Masuk sekarang',
withGitHub: 'Lanjutkan dengan GitHub', withGitHub: 'Lanjutkan dengan GitHub',
accountAlreadyInited: 'Akun sudah diinisialisasi', accountAlreadyInited: 'Akun sudah diinisialisasi',
withGoogle: 'Lanjutkan dengan Google', 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', invitationCode: 'Kode Undangan',
invalidToken: 'Token tidak valid atau kedaluwarsa', invalidToken: 'Token tidak valid atau kedaluwarsa',
setAdminAccount: 'Menyiapkan akun admin', 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.', 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', confirmPassword: 'Konfirmasi Kata Sandi',
changePasswordBtn: 'Menetapkan kata sandi',
changePasswordBtn: 'Tetapkan kata sandi',
resetPassword: 'Atur Ulang Kata Sandi', resetPassword: 'Atur Ulang Kata Sandi',
explore: 'Jelajahi Dify', explore: 'Jelajahi Dify',
useVerificationCode: 'Gunakan Kode Verifikasi', useVerificationCode: 'Gunakan Kode Verifikasi',
licenseLost: 'Lisensi Hilang', licenseLost: 'Lisensi Hilang',
licenseInactive: 'Lisensi Tidak Aktif', licenseInactive: 'Lisensi Tidak Aktif',
enterYourName: 'Silakan masukkan nama pengguna Anda', enterYourName: 'Silakan masukkan nama pengguna Anda',
back: 'Belakang',
back: 'Kembali',
activated: 'Masuk sekarang', activated: 'Masuk sekarang',
goToInit: 'Jika Anda belum menginisialisasi akun, silakan buka halaman inisialisasi', goToInit: 'Jika Anda belum menginisialisasi akun, silakan buka halaman inisialisasi',
licenseExpired: 'Lisensi Kedaluwarsa', licenseExpired: 'Lisensi Kedaluwarsa',
validate: 'Memvalidasi', validate: 'Memvalidasi',
resetPasswordDesc: 'Ketik email yang Anda gunakan untuk mendaftar di Dify dan kami akan mengirimkan email reset kata sandi kepada Anda.', 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.', licenseLostTip: 'Gagal menghubungkan server lisensi Dify. Hubungi administrator Anda untuk terus menggunakan Dify.',
signBtn: 'Tandatangan',
signBtn: 'Masuk',
sendResetLink: 'Kirim tautan reset', 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.', licenseExpiredTip: 'Lisensi Dify Enterprise untuk ruang kerja Anda telah kedaluwarsa. Hubungi administrator Anda untuk terus menggunakan Dify.',
email: 'Alamat email', email: 'Alamat email',
noLoginMethodTip: 'Silakan hubungi admin sistem untuk menambahkan metode autentikasi.', noLoginMethodTip: 'Silakan hubungi admin sistem untuk menambahkan metode autentikasi.',
licenseInactiveTip: 'Lisensi Dify Enterprise untuk ruang kerja Anda tidak aktif. Hubungi administrator Anda untuk terus menggunakan Dify.', licenseInactiveTip: 'Lisensi Dify Enterprise untuk ruang kerja Anda tidak aktif. Hubungi administrator Anda untuk terus menggunakan Dify.',
rightTitle: 'Buka potensi penuh LLM', rightTitle: 'Buka potensi penuh LLM',
welcome: '👋 Selamat datang di Dify, silakan login untuk melanjutkan.', 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?', forget: 'Lupa Kata Sandi Anda?',
backToLogin: 'Kembali ke login',
backToLogin: 'Kembali ke halaman masuk',
oneMoreStep: 'Satu langkah lagi', oneMoreStep: 'Satu langkah lagi',
} }



+ 5
- 5
web/i18n/id-ID/oauth.ts View File

authorizeFailed: 'Otorisasi gagal', authorizeFailed: 'Otorisasi gagal',
authAppInfoFetchFailed: 'Gagal mengambil info aplikasi untuk otorisasi', 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 export default translation

+ 5
- 5
web/i18n/id-ID/plugin.ts View File

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

+ 8
- 8
web/i18n/id-ID/time.ts View File

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

+ 13
- 13
web/i18n/id-ID/workflow.ts View File

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

+ 1
- 0
web/i18n/ro-RO/common.ts View File

providerManagedTip: 'Configurarea curentă este găzduită de furnizor.', providerManagedTip: 'Configurarea curentă este găzduită de furnizor.',
modelCredentials: 'Credențiale model', modelCredentials: 'Credențiale model',
specifyModelCredentialTip: 'Utilizați un acreditiv de model configurat.', specifyModelCredentialTip: 'Utilizați un acreditiv de model configurat.',
addNewModelCredential: 'Adăugați acreditive noi pentru model',
}, },
}, },
dataSource: { dataSource: {

+ 1
- 0
web/i18n/th-TH/common.ts View File

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

+ 1
- 1
web/i18n/tr-TR/common.ts View File

content: 'Geri Bildirim İçeriği', content: 'Geri Bildirim İçeriği',
subtitle: 'Lütfen bu yanıtla ilgili neyin yanlış gittiğini bize bildirin', subtitle: 'Lütfen bu yanıtla ilgili neyin yanlış gittiğini bize bildirin',
title: 'Geri Bildirim Sağla', 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 View File

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



+ 2
- 2
web/package.json View File

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

Loading…
Cancel
Save