mirror of
https://github.com/langgenius/dify.git
synced 2025-12-27 18:12:29 +00:00
Merge branch 'main' into feat/r2
This commit is contained in:
commit
b277acc298
28
.github/workflows/deploy-rag-dev.yml
vendored
Normal file
28
.github/workflows/deploy-rag-dev.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
name: Deploy RAG Dev
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Build and Push API & Web"]
|
||||
branches:
|
||||
- "deploy/rag-dev"
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.event.workflow_run.conclusion == 'success' &&
|
||||
github.event.workflow_run.head_branch == 'deploy/rag-dev'
|
||||
steps:
|
||||
- name: Deploy to server
|
||||
uses: appleboy/ssh-action@v0.1.8
|
||||
with:
|
||||
host: ${{ secrets.RAG_SSH_HOST }}
|
||||
username: ${{ secrets.SSH_USER }}
|
||||
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
script: |
|
||||
${{ vars.SSH_SCRIPT || secrets.SSH_SCRIPT }}
|
||||
1
.github/workflows/expose_service_ports.sh
vendored
1
.github/workflows/expose_service_ports.sh
vendored
@ -10,6 +10,7 @@ yq eval '.services["elasticsearch"].ports += ["9200:9200"]' -i docker/docker-com
|
||||
yq eval '.services.couchbase-server.ports += ["8091-8096:8091-8096"]' -i docker/docker-compose.yaml
|
||||
yq eval '.services.couchbase-server.ports += ["11210:11210"]' -i docker/docker-compose.yaml
|
||||
yq eval '.services.tidb.ports += ["4000:4000"]' -i docker/tidb/docker-compose.yaml
|
||||
yq eval '.services.oceanbase.ports += ["2881:2881"]' -i docker/docker-compose.yaml
|
||||
yq eval '.services.opengauss.ports += ["6600:6600"]' -i docker/docker-compose.yaml
|
||||
|
||||
echo "Ports exposed for sandbox, weaviate, tidb, qdrant, chroma, milvus, pgvector, pgvecto-rs, elasticsearch, couchbase, opengauss"
|
||||
|
||||
16
.github/workflows/vdb-tests.yml
vendored
16
.github/workflows/vdb-tests.yml
vendored
@ -31,6 +31,13 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Free Disk Space
|
||||
uses: endersonmenezes/free-disk-space@v2
|
||||
with:
|
||||
remove_dotnet: true
|
||||
remove_haskell: true
|
||||
remove_tool_cache: true
|
||||
|
||||
- name: Setup UV and Python
|
||||
uses: ./.github/actions/setup-uv
|
||||
with:
|
||||
@ -59,7 +66,7 @@ jobs:
|
||||
tidb
|
||||
tiflash
|
||||
|
||||
- name: Set up Vector Stores (Weaviate, Qdrant, PGVector, Milvus, PgVecto-RS, Chroma, MyScale, ElasticSearch, Couchbase)
|
||||
- name: Set up Vector Stores (Weaviate, Qdrant, PGVector, Milvus, PgVecto-RS, Chroma, MyScale, ElasticSearch, Couchbase, OceanBase)
|
||||
uses: hoverkraft-tech/compose-action@v2.0.2
|
||||
with:
|
||||
compose-file: |
|
||||
@ -75,9 +82,12 @@ jobs:
|
||||
pgvector
|
||||
chroma
|
||||
elasticsearch
|
||||
oceanbase
|
||||
|
||||
- name: Check TiDB Ready
|
||||
run: uv run --project api python api/tests/integration_tests/vdb/tidb_vector/check_tiflash_ready.py
|
||||
- name: Check VDB Ready (TiDB, Oceanbase)
|
||||
run: |
|
||||
uv run --project api python api/tests/integration_tests/vdb/tidb_vector/check_tiflash_ready.py
|
||||
uv run --project api python api/tests/integration_tests/vdb/oceanbase/check_oceanbase_ready.py
|
||||
|
||||
- name: Test Vector Stores
|
||||
run: uv run --project api bash dev/pytest/pytest_vdb.sh
|
||||
|
||||
@ -27,7 +27,7 @@ from models.dataset import Dataset, DatasetCollectionBinding, DatasetMetadata, D
|
||||
from models.dataset import Document as DatasetDocument
|
||||
from models.model import Account, App, AppAnnotationSetting, AppMode, Conversation, MessageAnnotation
|
||||
from models.provider import Provider, ProviderModel
|
||||
from services.account_service import RegisterService, TenantService
|
||||
from services.account_service import AccountService, RegisterService, TenantService
|
||||
from services.clear_free_plan_tenant_expired_logs import ClearFreePlanTenantExpiredLogs
|
||||
from services.plugin.data_migration import PluginDataMigration
|
||||
from services.plugin.plugin_migration import PluginMigration
|
||||
@ -68,6 +68,7 @@ def reset_password(email, new_password, password_confirm):
|
||||
account.password = base64_password_hashed
|
||||
account.password_salt = base64_salt
|
||||
db.session.commit()
|
||||
AccountService.reset_login_error_rate_limit(email)
|
||||
click.echo(click.style("Password reset successfully.", fg="green"))
|
||||
|
||||
|
||||
|
||||
@ -47,7 +47,13 @@ class AppInfoApi(Resource):
|
||||
def get(self, app_model: App):
|
||||
"""Get app information"""
|
||||
tags = [tag.name for tag in app_model.tags]
|
||||
return {"name": app_model.name, "description": app_model.description, "tags": tags, "mode": app_model.mode}
|
||||
return {
|
||||
"name": app_model.name,
|
||||
"description": app_model.description,
|
||||
"tags": tags,
|
||||
"mode": app_model.mode,
|
||||
"author_name": app_model.author_name,
|
||||
}
|
||||
|
||||
|
||||
api.add_resource(AppParameterApi, "/parameters")
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import logging
|
||||
import time
|
||||
from collections.abc import Generator, Mapping, Sequence
|
||||
from typing import TYPE_CHECKING, Any, Optional, Union
|
||||
@ -33,6 +34,8 @@ from models.model import App, AppMode, Message, MessageAnnotation
|
||||
if TYPE_CHECKING:
|
||||
from core.file.models import File
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AppRunner:
|
||||
def get_pre_calculate_rest_tokens(
|
||||
@ -298,7 +301,7 @@ class AppRunner:
|
||||
)
|
||||
|
||||
def _handle_invoke_result_stream(
|
||||
self, invoke_result: Generator, queue_manager: AppQueueManager, agent: bool
|
||||
self, invoke_result: Generator[LLMResultChunk, None, None], queue_manager: AppQueueManager, agent: bool
|
||||
) -> None:
|
||||
"""
|
||||
Handle invoke result
|
||||
@ -317,18 +320,28 @@ class AppRunner:
|
||||
else:
|
||||
queue_manager.publish(QueueAgentMessageEvent(chunk=result), PublishFrom.APPLICATION_MANAGER)
|
||||
|
||||
text += result.delta.message.content
|
||||
message = result.delta.message
|
||||
if isinstance(message.content, str):
|
||||
text += message.content
|
||||
elif isinstance(message.content, list):
|
||||
for content in message.content:
|
||||
if not isinstance(content, str):
|
||||
# TODO(QuantumGhost): Add multimodal output support for easy ui.
|
||||
_logger.warning("received multimodal output, type=%s", type(content))
|
||||
text += content.data
|
||||
else:
|
||||
text += content # failback to str
|
||||
|
||||
if not model:
|
||||
model = result.model
|
||||
|
||||
if not prompt_messages:
|
||||
prompt_messages = result.prompt_messages
|
||||
prompt_messages = list(result.prompt_messages)
|
||||
|
||||
if result.delta.usage:
|
||||
usage = result.delta.usage
|
||||
|
||||
if not usage:
|
||||
if usage is None:
|
||||
usage = LLMUsage.empty_usage()
|
||||
|
||||
llm_result = LLMResult(
|
||||
|
||||
@ -48,6 +48,7 @@ from core.model_manager import ModelInstance
|
||||
from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta, LLMUsage
|
||||
from core.model_runtime.entities.message_entities import (
|
||||
AssistantPromptMessage,
|
||||
TextPromptMessageContent,
|
||||
)
|
||||
from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel
|
||||
from core.ops.entities.trace_entity import TraceTaskName
|
||||
@ -309,6 +310,23 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline):
|
||||
delta_text = chunk.delta.message.content
|
||||
if delta_text is None:
|
||||
continue
|
||||
if isinstance(chunk.delta.message.content, list):
|
||||
delta_text = ""
|
||||
for content in chunk.delta.message.content:
|
||||
logger.debug(
|
||||
"The content type %s in LLM chunk delta message content.: %r", type(content), content
|
||||
)
|
||||
if isinstance(content, TextPromptMessageContent):
|
||||
delta_text += content.data
|
||||
elif isinstance(content, str):
|
||||
delta_text += content # failback to str
|
||||
else:
|
||||
logger.warning(
|
||||
"Unsupported content type %s in LLM chunk delta message content.: %r",
|
||||
type(content),
|
||||
content,
|
||||
)
|
||||
continue
|
||||
|
||||
if not self._task_state.llm_result.prompt_messages:
|
||||
self._task_state.llm_result.prompt_messages = chunk.prompt_messages
|
||||
|
||||
@ -80,6 +80,23 @@ class OceanBaseVector(BaseVector):
|
||||
|
||||
self.delete()
|
||||
|
||||
vals = []
|
||||
params = self._client.perform_raw_text_sql("SHOW PARAMETERS LIKE '%ob_vector_memory_limit_percentage%'")
|
||||
for row in params:
|
||||
val = int(row[6])
|
||||
vals.append(val)
|
||||
if len(vals) == 0:
|
||||
raise ValueError("ob_vector_memory_limit_percentage not found in parameters.")
|
||||
if any(val == 0 for val in vals):
|
||||
try:
|
||||
self._client.perform_raw_text_sql("ALTER SYSTEM SET ob_vector_memory_limit_percentage = 30")
|
||||
except Exception as e:
|
||||
raise Exception(
|
||||
"Failed to set ob_vector_memory_limit_percentage. "
|
||||
+ "Maybe the database user has insufficient privilege.",
|
||||
e,
|
||||
)
|
||||
|
||||
cols = [
|
||||
Column("id", String(36), primary_key=True, autoincrement=False),
|
||||
Column("vector", VECTOR(self._vec_dim)),
|
||||
@ -110,22 +127,6 @@ class OceanBaseVector(BaseVector):
|
||||
+ "to support fulltext index and vector index in the same table",
|
||||
e,
|
||||
)
|
||||
vals = []
|
||||
params = self._client.perform_raw_text_sql("SHOW PARAMETERS LIKE '%ob_vector_memory_limit_percentage%'")
|
||||
for row in params:
|
||||
val = int(row[6])
|
||||
vals.append(val)
|
||||
if len(vals) == 0:
|
||||
raise ValueError("ob_vector_memory_limit_percentage not found in parameters.")
|
||||
if any(val == 0 for val in vals):
|
||||
try:
|
||||
self._client.perform_raw_text_sql("ALTER SYSTEM SET ob_vector_memory_limit_percentage = 30")
|
||||
except Exception as e:
|
||||
raise Exception(
|
||||
"Failed to set ob_vector_memory_limit_percentage. "
|
||||
+ "Maybe the database user has insufficient privilege.",
|
||||
e,
|
||||
)
|
||||
redis_client.set(collection_exist_cache_key, 1, ex=3600)
|
||||
|
||||
def _check_hybrid_search_support(self) -> bool:
|
||||
|
||||
@ -6,7 +6,7 @@ import json
|
||||
import logging
|
||||
from typing import Optional, Union
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.engine import Engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
@ -151,11 +151,11 @@ class SQLAlchemyWorkflowExecutionRepository(WorkflowExecutionRepository):
|
||||
existing = session.scalar(select(WorkflowRun).where(WorkflowRun.id == domain_model.id_))
|
||||
if not existing:
|
||||
# For new records, get the next sequence number
|
||||
stmt = select(WorkflowRun.sequence_number).where(
|
||||
stmt = select(func.max(WorkflowRun.sequence_number)).where(
|
||||
WorkflowRun.app_id == self._app_id,
|
||||
WorkflowRun.tenant_id == self._tenant_id,
|
||||
)
|
||||
max_sequence = session.scalar(stmt.order_by(WorkflowRun.sequence_number.desc()))
|
||||
max_sequence = session.scalar(stmt)
|
||||
db_model.sequence_number = (max_sequence or 0) + 1
|
||||
else:
|
||||
# For updates, keep the existing sequence number
|
||||
|
||||
@ -6,7 +6,6 @@ from pydantic import BaseModel, Field
|
||||
from core.model_runtime.entities.llm_entities import LLMUsage
|
||||
from core.rag.entities.citation_metadata import RetrievalSourceMetadata
|
||||
from core.workflow.entities.node_entities import NodeRunResult
|
||||
from core.workflow.entities.workflow_node_execution import WorkflowNodeExecutionStatus
|
||||
|
||||
|
||||
class RunCompletedEvent(BaseModel):
|
||||
@ -39,11 +38,3 @@ class RunRetryEvent(BaseModel):
|
||||
error: str = Field(..., description="error")
|
||||
retry_index: int = Field(..., description="Retry attempt number")
|
||||
start_at: datetime = Field(..., description="Retry start time")
|
||||
|
||||
|
||||
class SingleStepRetryEvent(NodeRunResult):
|
||||
"""Single step retry event"""
|
||||
|
||||
status: WorkflowNodeExecutionStatus = WorkflowNodeExecutionStatus.RETRY
|
||||
|
||||
elapsed_time: float = Field(..., description="elapsed time")
|
||||
|
||||
@ -525,6 +525,8 @@ class LLMNode(BaseNode[LLMNodeData]):
|
||||
# Set appropriate response format based on model capabilities
|
||||
self._set_response_format(completion_params, model_schema.parameter_rules)
|
||||
model_config_with_cred.parameters = completion_params
|
||||
# NOTE(-LAN-): This line modify the `self.node_data.model`, which is used in `_invoke_llm()`.
|
||||
node_data_model.completion_params = completion_params
|
||||
return model, model_config_with_cred
|
||||
|
||||
def _fetch_prompt_messages(
|
||||
|
||||
@ -42,10 +42,6 @@ from core.workflow.constants import (
|
||||
)
|
||||
|
||||
|
||||
class InvalidSelectorError(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
class UnsupportedSegmentTypeError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
@ -4,7 +4,6 @@ from . import (
|
||||
app_model_config,
|
||||
audio,
|
||||
base,
|
||||
completion,
|
||||
conversation,
|
||||
dataset,
|
||||
document,
|
||||
@ -19,7 +18,6 @@ __all__ = [
|
||||
"app_model_config",
|
||||
"audio",
|
||||
"base",
|
||||
"completion",
|
||||
"conversation",
|
||||
"dataset",
|
||||
"document",
|
||||
|
||||
@ -55,7 +55,3 @@ class MemberNotInTenantError(BaseServiceError):
|
||||
|
||||
class RoleAlreadyAssignedError(BaseServiceError):
|
||||
pass
|
||||
|
||||
|
||||
class RateLimitExceededError(BaseServiceError):
|
||||
pass
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
from services.errors.base import BaseServiceError
|
||||
|
||||
|
||||
class CompletionStoppedError(BaseServiceError):
|
||||
pass
|
||||
@ -30,11 +30,11 @@ def retry_document_indexing_task(dataset_id: str, document_ids: list[str]):
|
||||
logging.info(click.style("Dataset not found: {}".format(dataset_id), fg="red"))
|
||||
db.session.close()
|
||||
return
|
||||
|
||||
tenant_id = dataset.tenant_id
|
||||
for document_id in document_ids:
|
||||
retry_indexing_cache_key = "document_{}_is_retried".format(document_id)
|
||||
# check document limit
|
||||
features = FeatureService.get_features(dataset.tenant_id)
|
||||
features = FeatureService.get_features(tenant_id)
|
||||
try:
|
||||
if features.billing.enabled:
|
||||
vector_space = features.vector_space
|
||||
|
||||
@ -0,0 +1,49 @@
|
||||
import time
|
||||
|
||||
import pymysql
|
||||
|
||||
|
||||
def check_oceanbase_ready() -> bool:
|
||||
try:
|
||||
connection = pymysql.connect(
|
||||
host="localhost",
|
||||
port=2881,
|
||||
user="root",
|
||||
password="difyai123456",
|
||||
)
|
||||
affected_rows = connection.query("SELECT 1")
|
||||
return affected_rows == 1
|
||||
except Exception as e:
|
||||
print(f"Oceanbase is not ready. Exception: {e}")
|
||||
return False
|
||||
finally:
|
||||
if connection:
|
||||
connection.close()
|
||||
|
||||
|
||||
def main():
|
||||
max_attempts = 50
|
||||
retry_interval_seconds = 2
|
||||
is_oceanbase_ready = False
|
||||
for attempt in range(max_attempts):
|
||||
try:
|
||||
is_oceanbase_ready = check_oceanbase_ready()
|
||||
except Exception as e:
|
||||
print(f"Oceanbase is not ready. Exception: {e}")
|
||||
is_oceanbase_ready = False
|
||||
|
||||
if is_oceanbase_ready:
|
||||
break
|
||||
else:
|
||||
print(f"Attempt {attempt + 1} failed, retry in {retry_interval_seconds} seconds...")
|
||||
time.sleep(retry_interval_seconds)
|
||||
|
||||
if is_oceanbase_ready:
|
||||
print("Oceanbase is ready.")
|
||||
else:
|
||||
print(f"Oceanbase is not ready after {max_attempts} attempting checks.")
|
||||
exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -1,15 +1,11 @@
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from core.rag.datasource.vdb.oceanbase.oceanbase_vector import (
|
||||
OceanBaseVector,
|
||||
OceanBaseVectorConfig,
|
||||
)
|
||||
from tests.integration_tests.vdb.__mock.tcvectordb import setup_tcvectordb_mock
|
||||
from tests.integration_tests.vdb.test_vector_store import (
|
||||
AbstractVectorTest,
|
||||
get_example_text,
|
||||
setup_mock_redis,
|
||||
)
|
||||
|
||||
@ -20,10 +16,11 @@ def oceanbase_vector():
|
||||
"dify_test_collection",
|
||||
config=OceanBaseVectorConfig(
|
||||
host="127.0.0.1",
|
||||
port="2881",
|
||||
user="root@test",
|
||||
port=2881,
|
||||
user="root",
|
||||
database="test",
|
||||
password="test",
|
||||
password="difyai123456",
|
||||
enable_hybrid_search=True,
|
||||
),
|
||||
)
|
||||
|
||||
@ -33,39 +30,13 @@ class OceanBaseVectorTest(AbstractVectorTest):
|
||||
super().__init__()
|
||||
self.vector = vector
|
||||
|
||||
def search_by_vector(self):
|
||||
hits_by_vector = self.vector.search_by_vector(query_vector=self.example_embedding)
|
||||
assert len(hits_by_vector) == 0
|
||||
|
||||
def search_by_full_text(self):
|
||||
hits_by_full_text = self.vector.search_by_full_text(query=get_example_text())
|
||||
assert len(hits_by_full_text) == 0
|
||||
|
||||
def text_exists(self):
|
||||
exist = self.vector.text_exists(self.example_doc_id)
|
||||
assert exist == True
|
||||
|
||||
def get_ids_by_metadata_field(self):
|
||||
ids = self.vector.get_ids_by_metadata_field(key="document_id", value=self.example_doc_id)
|
||||
assert len(ids) == 0
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def setup_mock_oceanbase_client():
|
||||
with patch("core.rag.datasource.vdb.oceanbase.oceanbase_vector.ObVecClient", new_callable=MagicMock) as mock_client:
|
||||
yield mock_client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def setup_mock_oceanbase_vector(oceanbase_vector):
|
||||
with patch.object(oceanbase_vector, "_client"):
|
||||
yield oceanbase_vector
|
||||
assert len(ids) == 1
|
||||
|
||||
|
||||
def test_oceanbase_vector(
|
||||
setup_mock_redis,
|
||||
setup_mock_oceanbase_client,
|
||||
setup_mock_oceanbase_vector,
|
||||
oceanbase_vector,
|
||||
):
|
||||
OceanBaseVectorTest(oceanbase_vector).run_all_tests()
|
||||
|
||||
@ -1067,6 +1067,7 @@ PLUGIN_MEDIA_CACHE_PATH=assets
|
||||
# Plugin oss bucket
|
||||
PLUGIN_STORAGE_OSS_BUCKET=
|
||||
# Plugin oss s3 credentials
|
||||
PLUGIN_S3_USE_AWS=
|
||||
PLUGIN_S3_USE_AWS_MANAGED_IAM=false
|
||||
PLUGIN_S3_ENDPOINT=
|
||||
PLUGIN_S3_USE_PATH_STYLE=false
|
||||
|
||||
@ -168,6 +168,7 @@ services:
|
||||
PLUGIN_MEDIA_CACHE_PATH: ${PLUGIN_MEDIA_CACHE_PATH:-assets}
|
||||
PLUGIN_STORAGE_OSS_BUCKET: ${PLUGIN_STORAGE_OSS_BUCKET:-}
|
||||
S3_USE_AWS_MANAGED_IAM: ${PLUGIN_S3_USE_AWS_MANAGED_IAM:-false}
|
||||
S3_USE_AWS: ${PLUGIN_S3_USE_AWS:-}
|
||||
S3_ENDPOINT: ${PLUGIN_S3_ENDPOINT:-}
|
||||
S3_USE_PATH_STYLE: ${PLUGIN_S3_USE_PATH_STYLE:-false}
|
||||
AWS_ACCESS_KEY: ${PLUGIN_AWS_ACCESS_KEY:-}
|
||||
@ -434,7 +435,7 @@ services:
|
||||
|
||||
# OceanBase vector database
|
||||
oceanbase:
|
||||
image: oceanbase/oceanbase-ce:4.3.5.1-101000042025031818
|
||||
image: oceanbase/oceanbase-ce:4.3.5-lts
|
||||
container_name: oceanbase
|
||||
profiles:
|
||||
- oceanbase
|
||||
@ -449,9 +450,7 @@ services:
|
||||
OB_TENANT_PASSWORD: ${OCEANBASE_VECTOR_PASSWORD:-difyai123456}
|
||||
OB_CLUSTER_NAME: ${OCEANBASE_CLUSTER_NAME:-difyai}
|
||||
OB_SERVER_IP: 127.0.0.1
|
||||
MODE: MINI
|
||||
ports:
|
||||
- "${OCEANBASE_VECTOR_PORT:-2881}:2881"
|
||||
MODE: mini
|
||||
|
||||
# Oracle vector database
|
||||
oracle:
|
||||
|
||||
@ -103,6 +103,7 @@ services:
|
||||
PLUGIN_PACKAGE_CACHE_PATH: ${PLUGIN_PACKAGE_CACHE_PATH:-plugin_packages}
|
||||
PLUGIN_MEDIA_CACHE_PATH: ${PLUGIN_MEDIA_CACHE_PATH:-assets}
|
||||
PLUGIN_STORAGE_OSS_BUCKET: ${PLUGIN_STORAGE_OSS_BUCKET:-}
|
||||
S3_USE_AWS: ${PLUGIN_S3_USE_AWS:-}
|
||||
S3_USE_AWS_MANAGED_IAM: ${PLUGIN_S3_USE_AWS_MANAGED_IAM:-false}
|
||||
S3_ENDPOINT: ${PLUGIN_S3_ENDPOINT:-}
|
||||
S3_USE_PATH_STYLE: ${PLUGIN_S3_USE_PATH_STYLE:-false}
|
||||
|
||||
@ -467,6 +467,7 @@ x-shared-env: &shared-api-worker-env
|
||||
PLUGIN_PACKAGE_CACHE_PATH: ${PLUGIN_PACKAGE_CACHE_PATH:-plugin_packages}
|
||||
PLUGIN_MEDIA_CACHE_PATH: ${PLUGIN_MEDIA_CACHE_PATH:-assets}
|
||||
PLUGIN_STORAGE_OSS_BUCKET: ${PLUGIN_STORAGE_OSS_BUCKET:-}
|
||||
PLUGIN_S3_USE_AWS: ${PLUGIN_S3_USE_AWS:-}
|
||||
PLUGIN_S3_USE_AWS_MANAGED_IAM: ${PLUGIN_S3_USE_AWS_MANAGED_IAM:-false}
|
||||
PLUGIN_S3_ENDPOINT: ${PLUGIN_S3_ENDPOINT:-}
|
||||
PLUGIN_S3_USE_PATH_STYLE: ${PLUGIN_S3_USE_PATH_STYLE:-false}
|
||||
@ -674,6 +675,7 @@ services:
|
||||
PLUGIN_MEDIA_CACHE_PATH: ${PLUGIN_MEDIA_CACHE_PATH:-assets}
|
||||
PLUGIN_STORAGE_OSS_BUCKET: ${PLUGIN_STORAGE_OSS_BUCKET:-}
|
||||
S3_USE_AWS_MANAGED_IAM: ${PLUGIN_S3_USE_AWS_MANAGED_IAM:-false}
|
||||
S3_USE_AWS: ${PLUGIN_S3_USE_AWS:-}
|
||||
S3_ENDPOINT: ${PLUGIN_S3_ENDPOINT:-}
|
||||
S3_USE_PATH_STYLE: ${PLUGIN_S3_USE_PATH_STYLE:-false}
|
||||
AWS_ACCESS_KEY: ${PLUGIN_AWS_ACCESS_KEY:-}
|
||||
@ -940,7 +942,7 @@ services:
|
||||
|
||||
# OceanBase vector database
|
||||
oceanbase:
|
||||
image: oceanbase/oceanbase-ce:4.3.5.1-101000042025031818
|
||||
image: oceanbase/oceanbase-ce:4.3.5-lts
|
||||
container_name: oceanbase
|
||||
profiles:
|
||||
- oceanbase
|
||||
@ -955,9 +957,7 @@ services:
|
||||
OB_TENANT_PASSWORD: ${OCEANBASE_VECTOR_PASSWORD:-difyai123456}
|
||||
OB_CLUSTER_NAME: ${OCEANBASE_CLUSTER_NAME:-difyai}
|
||||
OB_SERVER_IP: 127.0.0.1
|
||||
MODE: MINI
|
||||
ports:
|
||||
- "${OCEANBASE_VECTOR_PORT:-2881}:2881"
|
||||
MODE: mini
|
||||
|
||||
# Oracle vector database
|
||||
oracle:
|
||||
|
||||
@ -133,6 +133,7 @@ PLUGIN_MEDIA_CACHE_PATH=assets
|
||||
PLUGIN_STORAGE_OSS_BUCKET=
|
||||
# Plugin oss s3 credentials
|
||||
PLUGIN_S3_USE_AWS_MANAGED_IAM=false
|
||||
PLUGIN_S3_USE_AWS=
|
||||
PLUGIN_S3_ENDPOINT=
|
||||
PLUGIN_S3_USE_PATH_STYLE=false
|
||||
PLUGIN_AWS_ACCESS_KEY=
|
||||
|
||||
@ -25,9 +25,8 @@ import Loading from '@/app/components/base/loading'
|
||||
import DatasetDetailContext from '@/context/dataset-detail'
|
||||
import { DataSourceType } from '@/models/datasets'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import { LanguagesSupported } from '@/i18n/language'
|
||||
import { useStore } from '@/app/components/app/store'
|
||||
import { getLocaleOnClient } from '@/i18n'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import LinkedAppsPanel from '@/app/components/base/linked-apps-panel'
|
||||
@ -45,9 +44,9 @@ type IExtraInfoProps = {
|
||||
}
|
||||
|
||||
const ExtraInfo = ({ isMobile, relatedApps, expand }: IExtraInfoProps) => {
|
||||
const locale = getLocaleOnClient()
|
||||
const [isShowTips, { toggle: toggleTips, set: setShowTips }] = useBoolean(!isMobile)
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
|
||||
const hasRelatedApps = relatedApps?.data && relatedApps?.data?.length > 0
|
||||
const relatedAppsTotal = relatedApps?.data?.length || 0
|
||||
@ -97,11 +96,7 @@ const ExtraInfo = ({ isMobile, relatedApps, expand }: IExtraInfoProps) => {
|
||||
<div className='my-2 text-xs text-text-tertiary'>{t('common.datasetMenus.emptyTip')}</div>
|
||||
<a
|
||||
className='mt-2 inline-flex cursor-pointer items-center text-xs text-text-accent'
|
||||
href={
|
||||
locale === LanguagesSupported[1]
|
||||
? 'https://docs.dify.ai/zh-hans/guides/knowledge-base/integrate-knowledge-within-application'
|
||||
: 'https://docs.dify.ai/guides/knowledge-base/integrate-knowledge-within-application'
|
||||
}
|
||||
href={docLink('/guides/knowledge-base/integrate-knowledge-within-application')}
|
||||
target='_blank' rel='noopener noreferrer'
|
||||
>
|
||||
<RiBookOpenLine className='mr-1 text-text-accent' />
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import OperationBtn from '@/app/components/app/configuration/base/operation-btn'
|
||||
import Panel from '@/app/components/app/configuration/base/feature-panel'
|
||||
import { MessageClockCircle } from '@/app/components/base/icons/src/vender/solid/general'
|
||||
import I18n from '@/context/i18n'
|
||||
import { LanguagesSupported } from '@/i18n/language'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
type Props = {
|
||||
showWarning: boolean
|
||||
@ -19,7 +17,7 @@ const HistoryPanel: FC<Props> = ({
|
||||
onShowEditModal,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { locale } = useContext(I18n)
|
||||
const docLink = useDocLink()
|
||||
|
||||
return (
|
||||
<Panel
|
||||
@ -45,9 +43,8 @@ const HistoryPanel: FC<Props> = ({
|
||||
{showWarning && (
|
||||
<div className='flex justify-between rounded-b-xl bg-background-section-burn px-3 py-2 text-xs text-text-secondary'>
|
||||
<div>{t('appDebug.feature.conversationHistory.tip')}
|
||||
<a href={`${locale === LanguagesSupported[1]
|
||||
? 'https://docs.dify.ai/zh-hans/learn-more/extended-reading/prompt-engineering/README'
|
||||
: 'https://docs.dify.ai/en/features/prompt-engineering'}`}
|
||||
<a href={docLink('/learn-more/extended-reading/what-is-llmops',
|
||||
{ 'zh-Hans': '/learn-more/extended-reading/prompt-engineering/README' })}
|
||||
target='_blank' rel='noopener noreferrer'
|
||||
className='text-[#155EEF]'>{t('appDebug.feature.conversationHistory.learnMore')}
|
||||
</a>
|
||||
|
||||
@ -31,6 +31,7 @@ import {
|
||||
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { fetchMembers } from '@/service/common'
|
||||
import type { Member } from '@/models/common'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
type SettingsModalProps = {
|
||||
currentDataset: DataSet
|
||||
@ -58,6 +59,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
|
||||
currentModel: isRerankDefaultModelValid,
|
||||
} = useModelListAndDefaultModelAndCurrentProviderAndModel(ModelTypeEnum.rerank)
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
const { notify } = useToastContext()
|
||||
const ref = useRef(null)
|
||||
const isExternal = currentDataset.provider === 'external'
|
||||
@ -328,7 +330,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
|
||||
<div>
|
||||
<div className='system-sm-semibold text-text-secondary'>{t('datasetSettings.form.retrievalSetting.title')}</div>
|
||||
<div className='text-xs font-normal leading-[18px] text-text-tertiary'>
|
||||
<a target='_blank' rel='noopener noreferrer' href='https://docs.dify.ai/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings' className='text-text-accent'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
|
||||
<a target='_blank' rel='noopener noreferrer' href={docLink('/guides/knowledge-base/create-knowledge-and-upload-documents/setting-indexing-methods#setting-the-retrieval-setting')} className='text-text-accent'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
|
||||
{t('datasetSettings.form.retrievalSetting.description')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -2,9 +2,7 @@
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import I18n from '@/context/i18n'
|
||||
import { LanguagesSupported } from '@/i18n/language'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
type Props = {
|
||||
onReturnToSimpleMode: () => void
|
||||
}
|
||||
@ -13,7 +11,7 @@ const AdvancedModeWarning: FC<Props> = ({
|
||||
onReturnToSimpleMode,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { locale } = useContext(I18n)
|
||||
const docLink = useDocLink()
|
||||
const [show, setShow] = React.useState(true)
|
||||
if (!show)
|
||||
return null
|
||||
@ -25,7 +23,7 @@ const AdvancedModeWarning: FC<Props> = ({
|
||||
<span className='text-gray-700'>{t('appDebug.promptMode.advancedWarning.description')}</span>
|
||||
<a
|
||||
className='font-medium text-[#155EEF]'
|
||||
href={`https://docs.dify.ai/${locale === LanguagesSupported[1] ? '/guides/features/prompt-engineering' : 'features/prompt-engineering'}`}
|
||||
href={docLink('/guides/features/prompt-engineering')}
|
||||
target='_blank' rel='noopener noreferrer'
|
||||
>
|
||||
{t('appDebug.promptMode.advancedWarning.learnMore')}
|
||||
|
||||
@ -29,6 +29,7 @@ import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
||||
import { getRedirection } from '@/utils/app-redirection'
|
||||
import FullScreenModal from '@/app/components/base/fullscreen-modal'
|
||||
import useTheme from '@/hooks/use-theme'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
type CreateAppProps = {
|
||||
onSuccess: () => void
|
||||
@ -303,31 +304,33 @@ function AppTypeCard({ icon, title, description, active, onClick }: AppTypeCardP
|
||||
|
||||
function AppPreview({ mode }: { mode: AppMode }) {
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
const modeToPreviewInfoMap = {
|
||||
'chat': {
|
||||
title: t('app.types.chatbot'),
|
||||
description: t('app.newApp.chatbotUserDescription'),
|
||||
link: 'https://docs.dify.ai/guides/application-orchestrate/readme',
|
||||
link: docLink('/guides/application-orchestrate/chatbot-application'),
|
||||
},
|
||||
'advanced-chat': {
|
||||
title: t('app.types.advanced'),
|
||||
description: t('app.newApp.advancedUserDescription'),
|
||||
link: 'https://docs.dify.ai/en/guides/workflow/README',
|
||||
link: docLink('/guides/workflow/readme'),
|
||||
},
|
||||
'agent-chat': {
|
||||
title: t('app.types.agent'),
|
||||
description: t('app.newApp.agentUserDescription'),
|
||||
link: 'https://docs.dify.ai/en/guides/application-orchestrate/agent',
|
||||
link: docLink('/guides/application-orchestrate/agent'),
|
||||
},
|
||||
'completion': {
|
||||
title: t('app.newApp.completeApp'),
|
||||
description: t('app.newApp.completionUserDescription'),
|
||||
link: null,
|
||||
link: docLink('/guides/application-orchestrate/text-generator',
|
||||
{ 'zh-Hans': '/guides/application-orchestrate/readme' }),
|
||||
},
|
||||
'workflow': {
|
||||
title: t('app.types.workflow'),
|
||||
description: t('app.newApp.workflowUserDescription'),
|
||||
link: 'https://docs.dify.ai/en/guides/workflow/README',
|
||||
link: docLink('/guides/workflow/readme'),
|
||||
},
|
||||
}
|
||||
const previewInfo = modeToPreviewInfoMap[mode]
|
||||
|
||||
@ -354,7 +354,8 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
adjustModalWidth()
|
||||
const raf = requestAnimationFrame(adjustModalWidth)
|
||||
return () => cancelAnimationFrame(raf)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
|
||||
@ -3,13 +3,11 @@ import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { ArrowTopRightOnSquareIcon } from '@heroicons/react/24/outline'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import type { AppMode } from '@/types/app'
|
||||
import I18n from '@/context/i18n'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Tag from '@/app/components/base/tag'
|
||||
import { LanguagesSupported } from '@/i18n/language'
|
||||
|
||||
type IShareLinkProps = {
|
||||
isShow: boolean
|
||||
@ -43,7 +41,7 @@ const CustomizeModal: FC<IShareLinkProps> = ({
|
||||
mode,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { locale } = useContext(I18n)
|
||||
const docLink = useDocLink()
|
||||
const isChatApp = mode === 'chat' || mode === 'advanced-chat'
|
||||
|
||||
return <Modal
|
||||
@ -101,10 +99,7 @@ const CustomizeModal: FC<IShareLinkProps> = ({
|
||||
className='mt-2'
|
||||
onClick={() =>
|
||||
window.open(
|
||||
`https://docs.dify.ai/${locale !== LanguagesSupported[1]
|
||||
? 'user-guide/launching-dify-apps/developing-with-apis'
|
||||
: `${locale.toLowerCase()}/guides/application-publishing/developing-with-apis`
|
||||
}`,
|
||||
docLink('/guides/application-publishing/developing-with-apis'),
|
||||
'_blank',
|
||||
)
|
||||
}
|
||||
|
||||
@ -4,7 +4,6 @@ import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { RiArrowRightSLine, RiCloseLine } from '@remixicon/react'
|
||||
import Link from 'next/link'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { SparklesSoft } from '@/app/components/base/icons/src/public/common'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
@ -19,14 +18,14 @@ import { SimpleSelect } from '@/app/components/base/select'
|
||||
import type { AppDetailResponse } from '@/models/app'
|
||||
import type { AppIconType, AppSSO, Language } from '@/types/app'
|
||||
import { useToastContext } from '@/app/components/base/toast'
|
||||
import { LanguagesSupported, languages } from '@/i18n/language'
|
||||
import { languages } from '@/i18n/language'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import type { AppIconSelection } from '@/app/components/base/app-icon-picker'
|
||||
import AppIconPicker from '@/app/components/base/app-icon-picker'
|
||||
import I18n from '@/context/i18n'
|
||||
import cn from '@/utils/classnames'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
export type ISettingsModalProps = {
|
||||
isChat: boolean
|
||||
@ -98,7 +97,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
||||
const [language, setLanguage] = useState(default_language)
|
||||
const [saveLoading, setSaveLoading] = useState(false)
|
||||
const { t } = useTranslation()
|
||||
const { locale } = useContext(I18n)
|
||||
const docLink = useDocLink()
|
||||
|
||||
const [showAppIconPicker, setShowAppIconPicker] = useState(false)
|
||||
const [appIcon, setAppIcon] = useState<AppIconSelection>(
|
||||
@ -238,7 +237,8 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
||||
</div>
|
||||
<div className='system-xs-regular mt-0.5 text-text-tertiary'>
|
||||
<span>{t(`${prefixSettings}.modalTip`)}</span>
|
||||
<Link href={`${locale === LanguagesSupported[1] ? 'https://docs.dify.ai/zh-hans/guides/application-publishing/launch-your-webapp-quickly#she-zhi-ni-de-ai-zhan-dian' : 'https://docs.dify.ai/en/guides/application-publishing/launch-your-webapp-quickly/README'}`} target='_blank' rel='noopener noreferrer' className='text-text-accent'>{t('common.operation.learnMore')}</Link>
|
||||
<Link href={docLink('/guides/application-publishing/launch-your-webapp-quickly/README')}
|
||||
target='_blank' rel='noopener noreferrer' className='text-text-accent'>{t('common.operation.learnMore')}</Link>
|
||||
</div>
|
||||
</div>
|
||||
{/* form body */}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { RiCloseLine, RiInformation2Fill } from '@remixicon/react'
|
||||
import DialogWrapper from '@/app/components/base/features/new-feature-panel/dialog-wrapper'
|
||||
import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
@ -19,8 +18,7 @@ import Moderation from '@/app/components/base/features/new-feature-panel/moderat
|
||||
import AnnotationReply from '@/app/components/base/features/new-feature-panel/annotation-reply'
|
||||
import type { PromptVariable } from '@/models/debug'
|
||||
import type { InputVar } from '@/app/components/workflow/types'
|
||||
import I18n from '@/context/i18n'
|
||||
import { LanguagesSupported } from '@/i18n/language'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
type Props = {
|
||||
show: boolean
|
||||
@ -48,7 +46,7 @@ const NewFeaturePanel = ({
|
||||
onAutoAddPromptVariable,
|
||||
}: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const { locale } = useContext(I18n)
|
||||
const docLink = useDocLink()
|
||||
const { data: speech2textDefaultModel } = useDefaultModel(ModelTypeEnum.speech2text)
|
||||
const { data: text2speechDefaultModel } = useDefaultModel(ModelTypeEnum.tts)
|
||||
|
||||
@ -80,7 +78,7 @@ const NewFeaturePanel = ({
|
||||
<span>{isChatMode ? t('workflow.common.fileUploadTip') : t('workflow.common.ImageUploadLegacyTip')}</span>
|
||||
<a
|
||||
className='text-text-accent'
|
||||
href={`https://docs.dify.ai/${locale === LanguagesSupported[1] ? 'v/zh-hans/' : ''}guides/workflow/bulletin`}
|
||||
href={docLink('/guides/workflow/bulletin')}
|
||||
target='_blank' rel='noopener noreferrer'
|
||||
>{t('workflow.common.featuresDocLink')}</a>
|
||||
</div>
|
||||
|
||||
@ -14,7 +14,7 @@ const MarkdownButton = ({ node }: any) => {
|
||||
size={size}
|
||||
className={cn('!h-auto min-h-8 select-none whitespace-normal !px-3')}
|
||||
onClick={() => {
|
||||
if (isValidUrl(link)) {
|
||||
if (link && isValidUrl(link)) {
|
||||
window.open(link, '_blank')
|
||||
return
|
||||
}
|
||||
|
||||
@ -63,6 +63,7 @@ import CustomDialog from '@/app/components/base/dialog'
|
||||
import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem'
|
||||
import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
|
||||
import { noop } from 'lodash-es'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
const TextLabel: FC<PropsWithChildren> = (props) => {
|
||||
return <label className='system-sm-semibold text-text-secondary'>{props.children}</label>
|
||||
@ -146,6 +147,7 @@ const StepTwo = ({
|
||||
updateRetrievalMethodCache,
|
||||
}: StepTwoProps) => {
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
const { locale } = useContext(I18n)
|
||||
const media = useBreakpoints()
|
||||
const isMobile = media === MediaType.mobile
|
||||
@ -962,7 +964,9 @@ const StepTwo = ({
|
||||
<div className={'mb-1'}>
|
||||
<div className='system-md-semibold mb-0.5 text-text-secondary'>{t('datasetSettings.form.retrievalSetting.title')}</div>
|
||||
<div className='body-xs-regular text-text-tertiary'>
|
||||
<a target='_blank' rel='noopener noreferrer' href='https://docs.dify.ai/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings' className='text-text-accent'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
|
||||
<a target='_blank' rel='noopener noreferrer'
|
||||
href={docLink('/guides/knowledge-base/create-knowledge-and-upload-documents')}
|
||||
className='text-text-accent'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
|
||||
{t('datasetSettings.form.retrievalSetting.longDescription')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -4,6 +4,7 @@ import React, { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Input from './input'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
const I18N_PREFIX = 'datasetCreation.stepOne.website'
|
||||
|
||||
@ -17,6 +18,7 @@ const UrlInput: FC<Props> = ({
|
||||
onRun,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
const [url, setUrl] = useState('')
|
||||
const handleUrlChange = useCallback((url: string | number) => {
|
||||
setUrl(url as string)
|
||||
@ -32,7 +34,7 @@ const UrlInput: FC<Props> = ({
|
||||
<Input
|
||||
value={url}
|
||||
onChange={handleUrlChange}
|
||||
placeholder='https://docs.dify.ai'
|
||||
placeholder={docLink()}
|
||||
/>
|
||||
<Button
|
||||
variant='primary'
|
||||
|
||||
@ -4,6 +4,7 @@ import React, { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Input from './input'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
const I18N_PREFIX = 'datasetCreation.stepOne.website'
|
||||
|
||||
@ -17,6 +18,7 @@ const UrlInput: FC<Props> = ({
|
||||
onRun,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
const [url, setUrl] = useState('')
|
||||
const handleUrlChange = useCallback((url: string | number) => {
|
||||
setUrl(url as string)
|
||||
@ -32,7 +34,7 @@ const UrlInput: FC<Props> = ({
|
||||
<Input
|
||||
value={url}
|
||||
onChange={handleUrlChange}
|
||||
placeholder='https://docs.dify.ai'
|
||||
placeholder={docLink()}
|
||||
/>
|
||||
<Button
|
||||
variant='primary'
|
||||
|
||||
@ -29,8 +29,7 @@ import { useChildSegmentListKey, useSegmentListKey } from '@/service/knowledge/u
|
||||
import useEditDocumentMetadata from '../metadata/hooks/use-edit-dataset-metadata'
|
||||
import DatasetMetadataDrawer from '../metadata/metadata-dataset/dataset-metadata-drawer'
|
||||
import StatusWithAction from '../common/document-status-with-action/status-with-action'
|
||||
import { LanguagesSupported } from '@/i18n/language'
|
||||
import { getLocaleOnClient } from '@/i18n'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
const FolderPlusIcon = ({ className }: React.SVGProps<SVGElement>) => {
|
||||
return <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
|
||||
@ -86,6 +85,7 @@ const DEFAULT_LIMIT = 10
|
||||
|
||||
const Documents: FC<IDocumentsProps> = ({ datasetId }) => {
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
const { plan } = useProviderContext()
|
||||
const isFreePlan = plan.type === 'sandbox'
|
||||
const [inputValue, setInputValue] = useState<string>('') // the input value
|
||||
@ -100,7 +100,6 @@ const Documents: FC<IDocumentsProps> = ({ datasetId }) => {
|
||||
const isDataSourceWeb = dataset?.data_source_type === DataSourceType.WEB
|
||||
const isDataSourceFile = dataset?.data_source_type === DataSourceType.FILE
|
||||
const embeddingAvailable = !!dataset?.embedding_available
|
||||
const locale = getLocaleOnClient()
|
||||
const debouncedSearchValue = useDebounce(searchValue, { wait: 500 })
|
||||
|
||||
const { data: documentsRes, isFetching: isListLoading } = useDocumentList({
|
||||
@ -262,11 +261,7 @@ const Documents: FC<IDocumentsProps> = ({ datasetId }) => {
|
||||
<a
|
||||
className='flex items-center text-text-accent'
|
||||
target='_blank'
|
||||
href={
|
||||
locale === LanguagesSupported[1]
|
||||
? 'https://docs.dify.ai/zh-hans/guides/knowledge-base/integrate-knowledge-within-application'
|
||||
: 'https://docs.dify.ai/en/guides/knowledge-base/integrate-knowledge-within-application'
|
||||
}
|
||||
href={docLink('/guides/knowledge-base/integrate-knowledge-within-application')}
|
||||
>
|
||||
<span>{t('datasetDocuments.list.learnMore')}</span>
|
||||
<RiExternalLinkLine className='h-3 w-3' />
|
||||
|
||||
@ -5,6 +5,7 @@ import { RiBookOpenLine } from '@remixicon/react'
|
||||
import type { CreateExternalAPIReq, FormSchema } from '../declarations'
|
||||
import Input from '@/app/components/base/input'
|
||||
import cn from '@/utils/classnames'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
type FormProps = {
|
||||
className?: string
|
||||
@ -26,6 +27,7 @@ const Form: FC<FormProps> = React.memo(({
|
||||
inputClassName,
|
||||
}) => {
|
||||
const { t, i18n } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
const [changeKey, setChangeKey] = useState('')
|
||||
|
||||
const handleFormChange = (key: string, val: string) => {
|
||||
@ -57,7 +59,7 @@ const Form: FC<FormProps> = React.memo(({
|
||||
</label>
|
||||
{variable === 'endpoint' && (
|
||||
<a
|
||||
href={'https://docs.dify.ai/guides/knowledge-base/external-knowledge-api-documentation' || '/'}
|
||||
href={docLink('/guides/knowledge-base/external-knowledge-api-documentation') || '/'}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='body-xs-regular flex items-center text-text-accent'
|
||||
|
||||
@ -12,6 +12,7 @@ import ActionButton from '@/app/components/base/action-button'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
type ExternalAPIPanelProps = {
|
||||
onClose: () => void
|
||||
@ -19,6 +20,7 @@ type ExternalAPIPanelProps = {
|
||||
|
||||
const ExternalAPIPanel: React.FC<ExternalAPIPanelProps> = ({ onClose }) => {
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
const { setShowExternalKnowledgeAPIModal } = useModalContext()
|
||||
const { externalKnowledgeApiList, mutateExternalKnowledgeApis, isLoading } = useExternalKnowledgeApi()
|
||||
|
||||
@ -50,7 +52,8 @@ const ExternalAPIPanel: React.FC<ExternalAPIPanelProps> = ({ onClose }) => {
|
||||
<div className='flex grow flex-col items-start gap-1'>
|
||||
<div className='system-xl-semibold self-stretch text-text-primary'>{t('dataset.externalAPIPanelTitle')}</div>
|
||||
<div className='body-xs-regular self-stretch text-text-tertiary'>{t('dataset.externalAPIPanelDescription')}</div>
|
||||
<a className='flex cursor-pointer items-center justify-center gap-1 self-stretch' href='https://docs.dify.ai/guides/knowledge-base/external-knowledge-api-documentation' target='_blank'>
|
||||
<a className='flex cursor-pointer items-center justify-center gap-1 self-stretch'
|
||||
href={docLink('/guides/knowledge-base/external-knowledge-api-documentation')} target='_blank'>
|
||||
<RiBookOpenLine className='h-3 w-3 text-text-accent' />
|
||||
<div className='body-xs-regular grow text-text-accent'>{t('dataset.externalAPIPanelDocumentation')}</div>
|
||||
</a>
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import { RiBookOpenLine } from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
const InfoPanel = () => {
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
|
||||
return (
|
||||
<div className='flex w-[360px] flex-col items-start pb-2 pr-8 pt-[108px]'>
|
||||
@ -16,12 +18,15 @@ const InfoPanel = () => {
|
||||
</span>
|
||||
<span className='system-sm-regular text-text-tertiary'>
|
||||
{t('dataset.connectDatasetIntro.content.front')}
|
||||
<a className='system-sm-regular ml-1 text-text-accent' href='https://docs.dify.ai/en/guides/knowledge-base/external-knowledge-api' target='_blank' rel="noopener noreferrer">
|
||||
<a className='system-sm-regular ml-1 text-text-accent' href={docLink('/guides/knowledge-base/external-knowledge-api')} target='_blank' rel="noopener noreferrer">
|
||||
{t('dataset.connectDatasetIntro.content.link')}
|
||||
</a>
|
||||
{t('dataset.connectDatasetIntro.content.end')}
|
||||
</span>
|
||||
<a className='system-sm-regular self-stretch text-text-accent' href='https://docs.dify.ai/en/guides/knowledge-base/connect-external-knowledge-base' target='_blank' rel="noopener noreferrer">
|
||||
<a className='system-sm-regular self-stretch text-text-accent'
|
||||
href={docLink('/guides/knowledge-base/connect-external-knowledge-base')}
|
||||
target='_blank'
|
||||
rel="noopener noreferrer">
|
||||
{t('dataset.connectDatasetIntro.learnMore')}
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@ -11,6 +11,7 @@ import InfoPanel from './InfoPanel'
|
||||
import type { CreateKnowledgeBaseReq } from './declarations'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
type ExternalKnowledgeBaseCreateProps = {
|
||||
onConnect: (formValue: CreateKnowledgeBaseReq) => void
|
||||
@ -19,6 +20,7 @@ type ExternalKnowledgeBaseCreateProps = {
|
||||
|
||||
const ExternalKnowledgeBaseCreate: React.FC<ExternalKnowledgeBaseCreateProps> = ({ onConnect, loading }) => {
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
const router = useRouter()
|
||||
const [formData, setFormData] = useState<CreateKnowledgeBaseReq>({
|
||||
name: '',
|
||||
@ -59,7 +61,7 @@ const ExternalKnowledgeBaseCreate: React.FC<ExternalKnowledgeBaseCreateProps> =
|
||||
<span>{t('dataset.connectHelper.helper1')}</span>
|
||||
<span className='system-sm-medium text-text-secondary'>{t('dataset.connectHelper.helper2')}</span>
|
||||
<span>{t('dataset.connectHelper.helper3')}</span>
|
||||
<a className='system-sm-regular self-stretch text-text-accent' href='https://docs.dify.ai/en/guides/knowledge-base/connect-external-knowledge-base' target='_blank' rel="noopener noreferrer">
|
||||
<a className='system-sm-regular self-stretch text-text-accent' href={docLink('/guides/knowledge-base/connect-external-knowledge-base')} target='_blank' rel="noopener noreferrer">
|
||||
{t('dataset.connectHelper.helper4')}
|
||||
</a>
|
||||
<span>{t('dataset.connectHelper.helper5')} </span>
|
||||
|
||||
@ -11,6 +11,7 @@ import EconomicalRetrievalMethodConfig from '@/app/components/datasets/common/ec
|
||||
import Button from '@/app/components/base/button'
|
||||
import { isReRankModelSelected } from '@/app/components/datasets/common/check-rerank-model'
|
||||
import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
type Props = {
|
||||
indexMethod: string
|
||||
@ -29,6 +30,7 @@ const ModifyRetrievalModal: FC<Props> = ({
|
||||
}) => {
|
||||
const ref = useRef(null)
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
const [retrievalConfig, setRetrievalConfig] = useState(value)
|
||||
|
||||
// useClickAway(() => {
|
||||
@ -72,7 +74,7 @@ const ModifyRetrievalModal: FC<Props> = ({
|
||||
<a
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
href='https://docs.dify.ai/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings'
|
||||
href={docLink('/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings')}
|
||||
className='text-text-accent'
|
||||
>
|
||||
{t('datasetSettings.form.retrievalSetting.learnMore')}
|
||||
|
||||
@ -32,6 +32,7 @@ import { ModelTypeEnum } from '@/app/components/header/account-setting/model-pro
|
||||
import { fetchMembers } from '@/service/common'
|
||||
import type { Member } from '@/models/common'
|
||||
import AlertTriangle from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback/AlertTriangle'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
const rowClass = 'flex'
|
||||
const labelClass = `
|
||||
@ -46,6 +47,7 @@ const getKey = (pageIndex: number, previousPageData: DataSetListResponse) => {
|
||||
|
||||
const Form = () => {
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
const { notify } = useContext(ToastContext)
|
||||
const { mutate } = useSWRConfig()
|
||||
const { isCurrentWorkspaceDatasetOperator } = useAppContext()
|
||||
@ -308,7 +310,7 @@ const Form = () => {
|
||||
<div>
|
||||
<div className='system-sm-semibold text-text-secondary'>{t('datasetSettings.form.retrievalSetting.title')}</div>
|
||||
<div className='body-xs-regular text-text-tertiary'>
|
||||
<a target='_blank' rel='noopener noreferrer' href='https://docs.dify.ai/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings' className='text-text-accent'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
|
||||
<a target='_blank' rel='noopener noreferrer' href={docLink('/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings')} className='text-text-accent'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
|
||||
{t('datasetSettings.form.retrievalSetting.description')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -57,8 +57,8 @@ The text generation application offers non-session support and is ideal for tran
|
||||
<i>Due to Cloudflare restrictions, the request will be interrupted without a return after 100 seconds.</i>
|
||||
</Property>
|
||||
<Property name='user' type='string' key='user'>
|
||||
User identifier, used to define the identity of the end-user for retrieval and statistics.
|
||||
Should be uniquely defined by the developer within the application.
|
||||
User identifier, used to define the identity of the end-user, convenient for retrieval and statistics.
|
||||
The rules are defined by the developer and need to ensure that the user identifier is unique within the application. The Service API does not share conversations created by the WebApp.
|
||||
</Property>
|
||||
<Property name='files' type='array[object]' key='files'>
|
||||
File list, suitable for inputting files (images) combined with text understanding and answering questions, available only when the model supports Vision capability.
|
||||
@ -220,7 +220,7 @@ The text generation application offers non-session support and is ideal for tran
|
||||
- `file` (File) Required
|
||||
The file to be uploaded.
|
||||
- `user` (string) Required
|
||||
User identifier, defined by the developer's rules, must be unique within the application.
|
||||
User identifier, defined by the developer's rules, must be unique within the application. The Service API does not share conversations created by the WebApp.
|
||||
|
||||
### Response
|
||||
After a successful upload, the server will return the file's ID and related information.
|
||||
@ -290,7 +290,7 @@ The text generation application offers non-session support and is ideal for tran
|
||||
- `task_id` (string) Task ID, can be obtained from the streaming chunk return
|
||||
Request Body
|
||||
- `user` (string) Required
|
||||
User identifier, used to define the identity of the end-user, must be consistent with the user passed in the send message interface.
|
||||
User identifier, used to define the identity of the end-user, must be consistent with the user passed in the send message interface. The Service API does not share conversations created by the WebApp.
|
||||
### Response
|
||||
- `result` (string) Always returns "success"
|
||||
</Col>
|
||||
@ -512,6 +512,8 @@ The text generation application offers non-session support and is ideal for tran
|
||||
- `name` (string) application name
|
||||
- `description` (string) application description
|
||||
- `tags` (array[string]) application tags
|
||||
- `mode` (string) application mode
|
||||
- `author_name` (string) author name
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
@ -528,7 +530,9 @@ The text generation application offers non-session support and is ideal for tran
|
||||
"tags": [
|
||||
"tag1",
|
||||
"tag2"
|
||||
]
|
||||
],
|
||||
"mode": "chat",
|
||||
"author_name": "Dify"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@ -220,7 +220,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
- `file` (File) 必須
|
||||
アップロードするファイル。
|
||||
- `user` (string) 必須
|
||||
開発者のルールで定義されたユーザー識別子。アプリケーション内で一意である必要があります。
|
||||
開発者のルールで定義されたユーザー識別子。アプリケーション内で一意である必要があります。サービス API は WebApp によって作成された会話を共有しません。
|
||||
|
||||
### レスポンス
|
||||
アップロードが成功すると、サーバーはファイルの ID と関連情報を返します。
|
||||
@ -289,7 +289,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
- `task_id` (string) タスク ID、ストリーミングチャンクの返信から取得可能
|
||||
リクエストボディ
|
||||
- `user` (string) 必須
|
||||
ユーザー識別子。エンドユーザーの身元を定義するために使用され、メッセージ送信インターフェースで渡されたユーザーと一致する必要があります。
|
||||
ユーザー識別子。エンドユーザーの身元を定義するために使用され、メッセージ送信インターフェースで渡されたユーザーと一致する必要があります。サービス API は WebApp によって作成された会話を共有しません。
|
||||
### レスポンス
|
||||
- `result` (string) 常に"success"を返します
|
||||
</Col>
|
||||
@ -510,6 +510,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
- `name` (string) アプリケーションの名前
|
||||
- `description` (string) アプリケーションの説明
|
||||
- `tags` (array[string]) アプリケーションのタグ
|
||||
- `mode` (string) アプリケーションのモード
|
||||
- `author_name` (string) 作者の名前
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
@ -526,7 +528,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
"tags": [
|
||||
"tag1",
|
||||
"tag2"
|
||||
]
|
||||
],
|
||||
"mode": "chat",
|
||||
"author_name": "Dify"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@ -266,7 +266,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
|
||||
### Request Body
|
||||
- `user` (string) Required
|
||||
用户标识,用于定义终端用户的身份,必须和发送消息接口传入 user 保持一致。
|
||||
用户标识,用于定义终端用户的身份,必须和发送消息接口传入 user 保持一致。API 无法访问 WebApp 创建的会话。
|
||||
### Response
|
||||
- `result` (string) 固定返回 success
|
||||
</Col>
|
||||
@ -485,6 +485,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
- `name` (string) 应用名称
|
||||
- `description` (string) 应用描述
|
||||
- `tags` (array[string]) 应用标签
|
||||
- `mode` (string) 应用模式
|
||||
- 'author_name' (string) 作者名称
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
@ -501,7 +503,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
"tags": [
|
||||
"tag1",
|
||||
"tag2"
|
||||
]
|
||||
],
|
||||
"mode": "chat",
|
||||
"author_name": "Dify"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@ -59,7 +59,7 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
</Property>
|
||||
<Property name='user' type='string' key='user'>
|
||||
User identifier, used to define the identity of the end-user for retrieval and statistics.
|
||||
Should be uniquely defined by the developer within the application.
|
||||
Should be uniquely defined by the developer within the application. The Service API does not share conversations created by the WebApp.
|
||||
</Property>
|
||||
<Property name='conversation_id' type='string' key='conversation_id'>
|
||||
Conversation ID, to continue the conversation based on previous chat records, it is necessary to pass the previous message's conversation_id.
|
||||
@ -324,7 +324,7 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
- `file` (File) Required
|
||||
The file to be uploaded.
|
||||
- `user` (string) Required
|
||||
User identifier, defined by the developer's rules, must be unique within the application.
|
||||
User identifier, defined by the developer's rules, must be unique within the application. The Service API does not share conversations created by the WebApp.
|
||||
|
||||
### Response
|
||||
After a successful upload, the server will return the file's ID and related information.
|
||||
@ -394,7 +394,7 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
- `task_id` (string) Task ID, can be obtained from the streaming chunk return
|
||||
### Request Body
|
||||
- `user` (string) Required
|
||||
User identifier, used to define the identity of the end-user, must be consistent with the user passed in the send message interface.
|
||||
User identifier, used to define the identity of the end-user, must be consistent with the user passed in the message sending interface. The Service API does not share conversations created by the WebApp.
|
||||
### Response
|
||||
- `result` (string) Always returns "success"
|
||||
</Col>
|
||||
@ -448,7 +448,7 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
Upvote as `like`, downvote as `dislike`, revoke upvote as `null`
|
||||
</Property>
|
||||
<Property name='user' type='string' key='user'>
|
||||
User identifier, defined by the developer's rules, must be unique within the application.
|
||||
User identifier, defined by the developer's rules, must be unique within the application. The Service API does not share conversations created by the WebApp.
|
||||
</Property>
|
||||
<Property name='content' type='string' key='content'>
|
||||
The specific content of message feedback.
|
||||
@ -1123,6 +1123,8 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
- `name` (string) application name
|
||||
- `description` (string) application description
|
||||
- `tags` (array[string]) application tags
|
||||
- `mode` (string) application mode
|
||||
- `author_name` (string) application author name
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
@ -1139,7 +1141,9 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
"tags": [
|
||||
"tag1",
|
||||
"tag2"
|
||||
]
|
||||
],
|
||||
"mode": "advanced-chat",
|
||||
"author_name": "Dify"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@ -59,7 +59,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
</Property>
|
||||
<Property name='user' type='string' key='user'>
|
||||
ユーザー識別子、エンドユーザーの身元を定義するために使用され、統計のために使用されます。
|
||||
アプリケーション内で開発者によって一意に定義されるべきです。
|
||||
アプリケーション内で開発者によって一意に定義されるべきです。サービス API は WebApp によって作成された会話を共有しません。
|
||||
</Property>
|
||||
<Property name='conversation_id' type='string' key='conversation_id'>
|
||||
会話ID、以前のチャット記録に基づいて会話を続けるには、以前のメッセージのconversation_idを渡す必要があります。
|
||||
@ -324,7 +324,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
- `file` (File) 必須
|
||||
アップロードするファイル。
|
||||
- `user` (string) 必須
|
||||
ユーザー識別子、開発者のルールによって定義され、アプリケーション内で一意でなければなりません。
|
||||
ユーザー識別子、開発者のルールによって定義され、アプリケーション内で一意でなければなりません。サービス API は WebApp によって作成された会話を共有しません。
|
||||
|
||||
### 応答
|
||||
アップロードが成功すると、サーバーはファイルの ID と関連情報を返します。
|
||||
@ -394,7 +394,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
- `task_id` (string) タスク ID、ストリーミングチャンクの返り値から取得できます
|
||||
### リクエストボディ
|
||||
- `user` (string) 必須
|
||||
ユーザー識別子、エンドユーザーの身元を定義するために使用され、送信メッセージインターフェースで渡されたユーザーと一致している必要があります。
|
||||
ユーザー識別子、エンドユーザーの身元を定義するために使用され、送信メッセージインターフェースで渡されたユーザーと一致している必要があります。サービス API は WebApp によって作成された会話を共有しません。
|
||||
### 応答
|
||||
- `result` (string) 常に"success"を返します
|
||||
</Col>
|
||||
@ -1123,6 +1123,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
- `name` (string) アプリケーションの名前
|
||||
- `description` (string) アプリケーションの説明
|
||||
- `tags` (array[string]) アプリケーションのタグ
|
||||
- `mode` (string) アプリケーションのモード
|
||||
- `author_name` (string) 作者の名前
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
@ -1139,7 +1141,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
"tags": [
|
||||
"tag1",
|
||||
"tag2"
|
||||
]
|
||||
],
|
||||
"mode": "advanced-chat",
|
||||
"author_name": "Dify"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@ -56,7 +56,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
</Property>
|
||||
<Property name='user' type='string' key='user'>
|
||||
用户标识,用于定义终端用户的身份,方便检索、统计。
|
||||
由开发者定义规则,需保证用户标识在应用内唯一。
|
||||
由开发者定义规则,需保证用户标识在应用内唯一。服务 API 不会共享 WebApp 创建的对话。
|
||||
</Property>
|
||||
<Property name='conversation_id' type='string' key='conversation_id'>
|
||||
(选填)会话 ID,需要基于之前的聊天记录继续对话,必须传之前消息的 conversation_id。
|
||||
@ -402,7 +402,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
|
||||
### Request Body
|
||||
- `user` (string) Required
|
||||
用户标识,用于定义终端用户的身份,必须和发送消息接口传入 user 保持一致。
|
||||
用户标识,用于定义终端用户的身份,必须和发送消息接口传入 user 保持一致。API 无法访问 WebApp 创建的会话。
|
||||
### Response
|
||||
- `result` (string) 固定返回 success
|
||||
</Col>
|
||||
@ -1173,7 +1173,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
"tags": [
|
||||
"tag1",
|
||||
"tag2"
|
||||
]
|
||||
],
|
||||
"mode": "advanced-chat",
|
||||
"author_name": "Dify"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@ -287,7 +287,7 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
- `file` (File) Required
|
||||
The file to be uploaded.
|
||||
- `user` (string) Required
|
||||
User identifier, defined by the developer's rules, must be unique within the application.
|
||||
User identifier, defined by the developer's rules, must be unique within the application. The Service API does not share conversations created by the WebApp.
|
||||
|
||||
### Response
|
||||
After a successful upload, the server will return the file's ID and related information.
|
||||
@ -357,7 +357,7 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
- `task_id` (string) Task ID, can be obtained from the streaming chunk return
|
||||
### Request Body
|
||||
- `user` (string) Required
|
||||
User identifier, used to define the identity of the end-user, must be consistent with the user passed in the send message interface.
|
||||
User identifier, used to define the identity of the end-user, must be consistent with the user passed in the message sending interface. The Service API does not share conversations created by the WebApp.
|
||||
### Response
|
||||
- `result` (string) Always returns "success"
|
||||
</Col>
|
||||
@ -1151,6 +1151,8 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
- `name` (string) application name
|
||||
- `description` (string) application description
|
||||
- `tags` (array[string]) application tags
|
||||
- `mode` (string) application mode
|
||||
- `author_name` (string) application author name
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
@ -1167,7 +1169,9 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
"tags": [
|
||||
"tag1",
|
||||
"tag2"
|
||||
]
|
||||
],
|
||||
"mode": "advanced-chat",
|
||||
"author_name": "Dify"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@ -287,7 +287,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
- `file` (File) 必須
|
||||
アップロードするファイル。
|
||||
- `user` (string) 必須
|
||||
ユーザー識別子、開発者のルールで定義され、アプリケーション内で一意でなければなりません。
|
||||
ユーザー識別子、開発者のルールで定義され、アプリケーション内で一意でなければなりません。サービス API は WebApp によって作成された会話を共有しません。
|
||||
|
||||
### 応答
|
||||
アップロードが成功すると、サーバーはファイルの ID と関連情報を返します。
|
||||
@ -357,7 +357,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
- `task_id` (string) タスク ID、ストリーミングチャンクの返り値から取得できます
|
||||
### リクエストボディ
|
||||
- `user` (string) 必須
|
||||
ユーザー識別子、エンドユーザーのアイデンティティを定義するために使用され、メッセージ送信インターフェースで渡されたユーザーと一致している必要があります。
|
||||
ユーザー識別子、エンドユーザーのアイデンティティを定義するために使用され、メッセージ送信インターフェースで渡されたユーザーと一致している必要があります。サービス API は WebApp によって作成された会話を共有しません。
|
||||
### 応答
|
||||
- `result` (string) 常に"success"を返します
|
||||
</Col>
|
||||
@ -1150,6 +1150,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
- `name` (string) アプリケーションの名前
|
||||
- `description` (string) アプリケーションの説明
|
||||
- `tags` (array[string]) アプリケーションのタグ
|
||||
- `mode` (string) アプリケーションのモード
|
||||
- `author_name` (string) 作者の名前
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
@ -1166,7 +1168,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
"tags": [
|
||||
"tag1",
|
||||
"tag2"
|
||||
]
|
||||
],
|
||||
"mode": "chat",
|
||||
"author_name": "Dify"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@ -56,7 +56,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
</Property>
|
||||
<Property name='user' type='string' key='user'>
|
||||
用户标识,用于定义终端用户的身份,方便检索、统计。
|
||||
由开发者定义规则,需保证用户标识在应用内唯一。
|
||||
由开发者定义规则,需保证用户标识在应用内唯一。服务 API 不会共享 WebApp 创建的对话。
|
||||
</Property>
|
||||
<Property name='conversation_id' type='string' key='conversation_id'>
|
||||
(选填)会话 ID,需要基于之前的聊天记录继续对话,必须传之前消息的 conversation_id。
|
||||
@ -306,7 +306,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
要上传的文件。
|
||||
</Property>
|
||||
<Property name='user' type='string' key='user'>
|
||||
用户标识,用于定义终端用户的身份,必须和发送消息接口传入 user 保持一致。
|
||||
用户标识,用于定义终端用户的身份,必须和发送消息接口传入 user 保持一致。服务 API 不会共享 WebApp 创建的对话。
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
@ -373,7 +373,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
|
||||
### Request Body
|
||||
- `user` (string) Required
|
||||
用户标识,用于定义终端用户的身份,必须和发送消息接口传入 user 保持一致。
|
||||
用户标识,用于定义终端用户的身份,必须和发送消息接口传入 user 保持一致。API 无法访问 WebApp 创建的会话。
|
||||
### Response
|
||||
- `result` (string) 固定返回 success
|
||||
</Col>
|
||||
@ -425,7 +425,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
点赞 like, 点踩 dislike, 撤销点赞 null
|
||||
</Property>
|
||||
<Property name='user' type='string' key='user'>
|
||||
用户标识,由开发者定义规则,需保证用户标识在应用内唯一。
|
||||
用户标识,由开发者定义规则,需保证用户标识在应用内唯一。服务 API 不会共享 WebApp 创建的对话。
|
||||
</Property>
|
||||
<Property name='content' type='string' key='content'>
|
||||
消息反馈的具体信息。
|
||||
@ -1162,6 +1162,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
- `name` (string) 应用名称
|
||||
- `description` (string) 应用描述
|
||||
- `tags` (array[string]) 应用标签
|
||||
- `mode` (string) 应用模式
|
||||
- 'author_name' (string) 作者名称
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
@ -1178,7 +1180,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
"tags": [
|
||||
"tag1",
|
||||
"tag2"
|
||||
]
|
||||
],
|
||||
"mode": "chat",
|
||||
"author_name": "Dify"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@ -64,6 +64,8 @@ Workflow applications offers non-session support and is ideal for translation, a
|
||||
- `user` (string) Required
|
||||
User identifier, used to define the identity of the end-user for retrieval and statistics.
|
||||
Should be uniquely defined by the developer within the application.
|
||||
<br/>
|
||||
<i>The user identifier should be consistent with the user passed in the message sending interface. The Service API does not share conversations created by the WebApp.</i>
|
||||
|
||||
### Response
|
||||
When `response_mode` is `blocking`, return a CompletionResponse object.
|
||||
@ -401,7 +403,7 @@ Workflow applications offers non-session support and is ideal for translation, a
|
||||
- `task_id` (string) Task ID, can be obtained from the streaming chunk return
|
||||
### Request Body
|
||||
- `user` (string) Required
|
||||
User identifier, used to define the identity of the end-user, must be consistent with the user passed in the send message interface.
|
||||
User identifier, used to define the identity of the end-user, must be consistent with the user passed in the message sending interface. The Service API does not share conversations created by the WebApp.
|
||||
### Response
|
||||
- `result` (string) Always returns "success"
|
||||
</Col>
|
||||
@ -448,7 +450,7 @@ Workflow applications offers non-session support and is ideal for translation, a
|
||||
- `file` (File) Required
|
||||
The file to be uploaded.
|
||||
- `user` (string) Required
|
||||
User identifier, defined by the developer's rules, must be unique within the application.
|
||||
User identifier, defined by the developer's rules, must be unique within the application. The Service API does not share conversations created by the WebApp.
|
||||
|
||||
### Response
|
||||
After a successful upload, the server will return the file's ID and related information.
|
||||
@ -625,6 +627,8 @@ Workflow applications offers non-session support and is ideal for translation, a
|
||||
- `name` (string) application name
|
||||
- `description` (string) application description
|
||||
- `tags` (array[string]) application tags
|
||||
- `mode` (string) application mode
|
||||
- `author_name` (string) application author name
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
@ -641,7 +645,9 @@ Workflow applications offers non-session support and is ideal for translation, a
|
||||
"tags": [
|
||||
"tag1",
|
||||
"tag2"
|
||||
]
|
||||
],
|
||||
"mode": "workflow",
|
||||
"author_name": "Dify"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@ -404,7 +404,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
- `task_id` (string) タスク ID、ストリーミングチャンクの返り値から取得可能
|
||||
### リクエストボディ
|
||||
- `user` (string) 必須
|
||||
ユーザー識別子、エンドユーザーのアイデンティティを定義するために使用され、送信メッセージインターフェースで渡されたユーザーと一致している必要があります。
|
||||
ユーザー識別子、エンドユーザーのアイデンティティを定義するために使用され、送信メッセージインターフェースで渡されたユーザーと一致している必要があります。サービス API は WebApp によって作成された会話を共有しません。
|
||||
### 応答
|
||||
- `result` (string) 常に"success"を返します
|
||||
</Col>
|
||||
@ -451,7 +451,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
- `file` (File) 必須
|
||||
アップロードするファイル。
|
||||
- `user` (string) 必須
|
||||
ユーザー識別子、開発者のルールで定義され、アプリケーション内で一意でなければなりません。
|
||||
ユーザー識別子、開発者のルールで定義され、アプリケーション内で一意でなければなりません。サービス API は WebApp によって作成された会話を共有しません。
|
||||
|
||||
### 応答
|
||||
アップロードが成功すると、サーバーはファイルの ID と関連情報を返します。
|
||||
@ -628,6 +628,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
- `name` (string) アプリケーションの名前
|
||||
- `description` (string) アプリケーションの説明
|
||||
- `tags` (array[string]) アプリケーションのタグ
|
||||
- `mode` (string) アプリケーションのモード
|
||||
- `author_name` (string) 作者の名前
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
@ -644,7 +646,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
"tags": [
|
||||
"tag1",
|
||||
"tag2"
|
||||
]
|
||||
],
|
||||
"mode": "workflow",
|
||||
"author_name": "Dify"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@ -59,7 +59,7 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
|
||||
<i>由于 Cloudflare 限制,请求会在 100 秒超时无返回后中断。</i>
|
||||
- `user` (string) Required
|
||||
用户标识,用于定义终端用户的身份,方便检索、统计。
|
||||
由开发者定义规则,需保证用户标识在应用内唯一。
|
||||
由开发者定义规则,需保证用户标识在应用内唯一。API 无法访问 WebApp 创建的会话。
|
||||
|
||||
|
||||
### Response
|
||||
@ -394,7 +394,7 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
|
||||
- `task_id` (string) 任务 ID,可在流式返回 Chunk 中获取
|
||||
### Request Body
|
||||
- `user` (string) Required
|
||||
用户标识,用于定义终端用户的身份,必须和发送消息接口传入 user 保持一致。
|
||||
用户标识,用于定义终端用户的身份,必须和发送消息接口传入 user 保持一致。API 无法访问 WebApp 创建的会话。
|
||||
### Response
|
||||
- `result` (string) 固定返回 "success"
|
||||
</Col>
|
||||
@ -443,7 +443,7 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
|
||||
要上传的文件。
|
||||
</Property>
|
||||
<Property name='user' type='string' key='user'>
|
||||
用户标识,用于定义终端用户的身份,必须和发送消息接口传入 user 保持一致。
|
||||
用户标识,用于定义终端用户的身份,必须和发送消息接口传入 user 保持一致。服务 API 不会共享 WebApp 创建的对话。
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
@ -615,6 +615,8 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
|
||||
- `name` (string) 应用名称
|
||||
- `description` (string) 应用描述
|
||||
- `tags` (array[string]) 应用标签
|
||||
- `mode` (string) 应用模式
|
||||
- 'author_name' (string) 作者名称
|
||||
</Col>
|
||||
<Col>
|
||||
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info' \\\n-H 'Authorization: Bearer {api_key}'`}>
|
||||
@ -631,7 +633,9 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
|
||||
"tags": [
|
||||
"tag1",
|
||||
"tag2"
|
||||
]
|
||||
],
|
||||
"mode": "workflow",
|
||||
"author_name": "Dify"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@ -23,7 +23,6 @@ import GithubStar from '../github-star'
|
||||
import Support from './support'
|
||||
import Compliance from './compliance'
|
||||
import PremiumBadge from '@/app/components/base/premium-badge'
|
||||
import { useGetDocLanguage } from '@/context/i18n'
|
||||
import Avatar from '@/app/components/base/avatar'
|
||||
import ThemeSwitcher from '@/app/components/base/theme-switcher'
|
||||
import { logout } from '@/service/common'
|
||||
@ -33,6 +32,7 @@ import { useModalContext } from '@/context/modal-context'
|
||||
import { IS_CLOUD_EDITION } from '@/config'
|
||||
import cn from '@/utils/classnames'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
export default function AppSelector() {
|
||||
const itemClassName = `
|
||||
@ -44,10 +44,10 @@ export default function AppSelector() {
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
const { userProfile, langeniusVersionInfo, isCurrentWorkspaceOwner } = useAppContext()
|
||||
const { isEducationAccount } = useProviderContext()
|
||||
const { setShowAccountSettingModal } = useModalContext()
|
||||
const docLanguage = useGetDocLanguage()
|
||||
|
||||
const handleLogout = async () => {
|
||||
await logout({
|
||||
@ -133,7 +133,7 @@ export default function AppSelector() {
|
||||
className={cn(itemClassName, 'group justify-between',
|
||||
'data-[active]:bg-state-base-hover',
|
||||
)}
|
||||
href={`https://docs.dify.ai/${docLanguage}/introduction`}
|
||||
href={docLink('/introduction')}
|
||||
target='_blank' rel='noopener noreferrer'>
|
||||
<RiBookOpenLine className='size-4 shrink-0 text-text-tertiary' />
|
||||
<div className='system-md-regular grow px-1 text-text-secondary'>{t('common.userProfile.helpCenter')}</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import {
|
||||
RiAddLine,
|
||||
@ -20,8 +20,6 @@ import {
|
||||
useInvalidateEndpointList,
|
||||
} from '@/service/use-endpoints'
|
||||
import type { PluginDetail } from '@/app/components/plugins/types'
|
||||
import { LanguagesSupported } from '@/i18n/language'
|
||||
import I18n from '@/context/i18n'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type Props = {
|
||||
@ -29,7 +27,7 @@ type Props = {
|
||||
}
|
||||
const EndpointList = ({ detail }: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const { locale } = useContext(I18n)
|
||||
const docLink = useDocLink()
|
||||
const pluginUniqueID = detail.plugin_unique_identifier
|
||||
const declaration = detail.declaration.endpoint
|
||||
const showTopBorder = detail.declaration.tool
|
||||
@ -79,7 +77,7 @@ const EndpointList = ({ detail }: Props) => {
|
||||
</div>
|
||||
<div className='system-xs-regular text-text-tertiary'>{t('plugin.detailPanel.endpointsTip')}</div>
|
||||
<a
|
||||
href={`https://docs.dify.ai/${locale === LanguagesSupported[1] ? 'v/zh-hans/' : ''}plugins/schema-definition/endpoint`}
|
||||
href={docLink('/plugins/schema-definition/endpoint')}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
|
||||
@ -14,6 +14,7 @@ import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-m
|
||||
import { createCustomCollection } from '@/service/tools'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
type Props = {
|
||||
onRefreshData: () => void
|
||||
@ -25,10 +26,11 @@ const Contribute = ({ onRefreshData }: Props) => {
|
||||
const language = getLanguage(locale)
|
||||
const { isCurrentWorkspaceManager } = useAppContext()
|
||||
|
||||
const docLink = useDocLink()
|
||||
const linkUrl = useMemo(() => {
|
||||
if (language.startsWith('zh_'))
|
||||
return 'https://docs.dify.ai/zh-hans/guides/tools#ru-he-chuang-jian-zi-ding-yi-gong-ju'
|
||||
return 'https://docs.dify.ai/en/guides/tools#how-to-create-custom-tools'
|
||||
return docLink('/guides/tools#how-to-create-custom-tools', {
|
||||
'zh-Hans': '/guides/tools#ru-he-chuang-jian-zi-ding-yi-gong-ju',
|
||||
})
|
||||
}, [language])
|
||||
|
||||
const [isShowEditCollectionToolModal, setIsShowEditCustomCollectionModal] = useState(false)
|
||||
|
||||
@ -19,9 +19,7 @@ import { useWorkflowStore } from '../../../store'
|
||||
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
||||
import type { NodeOutPutVar } from '../../../types'
|
||||
import type { Node } from 'reactflow'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import I18n from '@/context/i18n'
|
||||
import { LanguagesSupported } from '@/i18n/language'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
export type Strategy = {
|
||||
agent_strategy_provider_name: string
|
||||
@ -52,7 +50,7 @@ type CustomField = ToolSelectorSchema | MultipleToolSelectorSchema
|
||||
export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
||||
const { strategy, onStrategyChange, formSchema, formValue, onFormValueChange, nodeOutputVars, availableNodes, nodeId } = props
|
||||
const { t } = useTranslation()
|
||||
const { locale } = useContext(I18n)
|
||||
const docLink = useDocLink()
|
||||
const defaultModel = useDefaultModel(ModelTypeEnum.textGeneration)
|
||||
const renderI18nObject = useRenderI18nObject()
|
||||
const workflowStore = useWorkflowStore()
|
||||
@ -223,11 +221,10 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
||||
title={t('workflow.nodes.agent.strategy.configureTip')}
|
||||
description={<div className='text-xs text-text-tertiary'>
|
||||
{t('workflow.nodes.agent.strategy.configureTipDesc')} <br />
|
||||
<Link href={
|
||||
locale === LanguagesSupported[1]
|
||||
? 'https://docs.dify.ai/zh-hans/guides/workflow/node/agent#xuan-ze-agent-ce-le'
|
||||
: 'https://docs.dify.ai/en/guides/workflow/node/agent#select-an-agent-strategy'
|
||||
} className='text-text-accent-secondary' target='_blank'>
|
||||
<Link href={docLink('/guides/workflow/node/agent#select-an-agent-strategy', {
|
||||
'zh-Hans': '/guides/workflow/node/agent#xuan-ze-agent-ce-le',
|
||||
})}
|
||||
className='text-text-accent-secondary' target='_blank'>
|
||||
{t('workflow.nodes.agent.learnMore')}
|
||||
</Link>
|
||||
</div>}
|
||||
|
||||
@ -5,6 +5,7 @@ import Input from '@/app/components/base/input'
|
||||
import { VarType } from '@/app/components/workflow/types'
|
||||
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
|
||||
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
type DefaultValueProps = {
|
||||
forms: DefaultValueForm[]
|
||||
@ -15,6 +16,7 @@ const DefaultValue = ({
|
||||
onFormChange,
|
||||
}: DefaultValueProps) => {
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
const getFormChangeHandler = useCallback(({ key, type }: DefaultValueForm) => {
|
||||
return (payload: any) => {
|
||||
let value
|
||||
@ -34,7 +36,7 @@ const DefaultValue = ({
|
||||
{t('workflow.nodes.common.errorHandle.defaultValue.desc')}
|
||||
|
||||
<a
|
||||
href='https://docs.dify.ai/en/guides/workflow/error-handling/README'
|
||||
href={docLink('/guides/workflow/error-handling/README')}
|
||||
target='_blank'
|
||||
className='text-text-accent'
|
||||
>
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import { RiMindMap } from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
const FailBranchCard = () => {
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
|
||||
return (
|
||||
<div className='px-4 pt-2'>
|
||||
@ -17,7 +19,7 @@ const FailBranchCard = () => {
|
||||
{t('workflow.nodes.common.errorHandle.failBranch.customizeTip')}
|
||||
|
||||
<a
|
||||
href='https://docs.dify.ai/guides/workflow/error-handling'
|
||||
href={docLink('/guides/workflow/error-handling/error-type')}
|
||||
target='_blank'
|
||||
className='text-text-accent'
|
||||
>
|
||||
|
||||
@ -2,12 +2,10 @@
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import VarReferenceVars from './var-reference-vars'
|
||||
import type { NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
|
||||
import ListEmpty from '@/app/components/base/list-empty'
|
||||
import { LanguagesSupported } from '@/i18n/language'
|
||||
import I18n from '@/context/i18n'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
type Props = {
|
||||
vars: NodeOutPutVar[]
|
||||
@ -24,7 +22,7 @@ const VarReferencePopup: FC<Props> = ({
|
||||
isSupportFileVar = true,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { locale } = useContext(I18n)
|
||||
const docLink = useDocLink()
|
||||
// max-h-[300px] overflow-y-auto todo: use portal to handle long list
|
||||
return (
|
||||
<div className='space-y-1 rounded-lg border border-components-panel-border bg-components-panel-bg p-1 shadow-lg' style={{
|
||||
@ -46,8 +44,10 @@ const VarReferencePopup: FC<Props> = ({
|
||||
description={<div className='system-xs-regular text-text-tertiary'>
|
||||
{t('workflow.variableReference.assignedVarsDescription')}
|
||||
<a target='_blank' rel='noopener noreferrer'
|
||||
className='text-text-accent-secondary'
|
||||
href={locale !== LanguagesSupported[1] ? 'https://docs.dify.ai/guides/workflow/variables#conversation-variables' : `https://docs.dify.ai/${locale.toLowerCase()}/guides/workflow/variables#hui-hua-bian-liang`}>{t('workflow.variableReference.conversationVars')}</a>
|
||||
className='text-text-accent-secondary'
|
||||
href={docLink('/guides/workflow/variables#conversation-variables', { 'zh-Hans': '/guides/workflow/variables#hui-hua-bian-liang' })}>
|
||||
{t('workflow.variableReference.conversationVars')}
|
||||
</a>
|
||||
</div>}
|
||||
/>
|
||||
))
|
||||
|
||||
@ -1,14 +1,12 @@
|
||||
import { useMemo } from 'react'
|
||||
import { useGetLanguage } from '@/context/i18n'
|
||||
import { useDocLink, useGetLanguage } from '@/context/i18n'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
|
||||
export const useNodeHelpLink = (nodeType: BlockEnum) => {
|
||||
const language = useGetLanguage()
|
||||
const docLink = useDocLink()
|
||||
const prefixLink = useMemo(() => {
|
||||
if (language === 'zh_Hans')
|
||||
return 'https://docs.dify.ai/zh-hans/guides/workflow/node/'
|
||||
|
||||
return 'https://docs.dify.ai/en/guides/workflow/node/'
|
||||
return docLink('/guides/workflow/node/')
|
||||
}, [language])
|
||||
const linkMap = useMemo(() => {
|
||||
if (language === 'zh_Hans') {
|
||||
|
||||
@ -21,8 +21,8 @@ import { MittProvider, VisualEditorContextProvider, useMittContext } from './vis
|
||||
import ErrorMessage from './error-message'
|
||||
import { useVisualEditorStore } from './visual-editor/store'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { useGetDocLanguage } from '@/context/i18n'
|
||||
import { JSON_SCHEMA_MAX_DEPTH } from '@/config'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
type JsonSchemaConfigProps = {
|
||||
defaultSchema?: SchemaRoot
|
||||
@ -53,7 +53,7 @@ const JsonSchemaConfig: FC<JsonSchemaConfigProps> = ({
|
||||
onClose,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const docLanguage = useGetDocLanguage()
|
||||
const docLink = useDocLink()
|
||||
const [currentTab, setCurrentTab] = useState(SchemaView.VisualEditor)
|
||||
const [jsonSchema, setJsonSchema] = useState(defaultSchema || DEFAULT_SCHEMA)
|
||||
const [json, setJson] = useState(JSON.stringify(jsonSchema, null, 2))
|
||||
@ -252,7 +252,7 @@ const JsonSchemaConfig: FC<JsonSchemaConfigProps> = ({
|
||||
<div className='flex items-center gap-x-2 p-6 pt-5'>
|
||||
<a
|
||||
className='flex grow items-center gap-x-1 text-text-accent'
|
||||
href={`https://docs.dify.ai/${docLanguage}/guides/workflow/structured-outputs`}
|
||||
href={docLink('/guides/workflow/structured-outputs')}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
|
||||
@ -3,7 +3,6 @@ import {
|
||||
useCallback,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import {
|
||||
useStoreApi,
|
||||
} from 'reactflow'
|
||||
@ -22,13 +21,12 @@ import type {
|
||||
import { findUsedVarNodes, updateNodeVars } from '@/app/components/workflow/nodes/_base/components/variable/utils'
|
||||
import { useNodesSyncDraft } from '@/app/components/workflow/hooks/use-nodes-sync-draft'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import I18n from '@/context/i18n'
|
||||
import { LanguagesSupported } from '@/i18n/language'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
const ChatVariablePanel = () => {
|
||||
const { t } = useTranslation()
|
||||
const { locale } = useContext(I18n)
|
||||
const docLink = useDocLink()
|
||||
const store = useStoreApi()
|
||||
const setShowChatVariablePanel = useStore(s => s.setShowChatVariablePanel)
|
||||
const varList = useStore(s => s.conversationVariables) as ConversationVariable[]
|
||||
@ -139,8 +137,11 @@ const ChatVariablePanel = () => {
|
||||
<div className='system-2xs-medium-uppercase inline-block rounded-[5px] border border-divider-deep px-[5px] py-[3px] text-text-tertiary'>TIPS</div>
|
||||
<div className='system-sm-regular mb-4 mt-1 text-text-secondary'>
|
||||
{t('workflow.chatVariable.panelDescription')}
|
||||
<a target='_blank' rel='noopener noreferrer' className='text-text-accent' href={locale !== LanguagesSupported[1] ? 'https://docs.dify.ai/guides/workflow/variables#conversation-variables' : `https://docs.dify.ai/${locale.toLowerCase()}/guides/workflow/variables#hui-hua-bian-liang`}>{t('workflow.chatVariable.docLink')}</a>
|
||||
</div>
|
||||
<a target='_blank' rel='noopener noreferrer' className='text-text-accent'
|
||||
href={docLink('/guides/workflow/variables#conversation-variables', { 'zh-Hans': '/guides/workflow/variables#hui-hua-bian-liang' })}>
|
||||
{t('workflow.chatVariable.docLink')}
|
||||
</a>
|
||||
</div>
|
||||
<div className='flex items-center gap-2'>
|
||||
<div className='radius-lg flex flex-col border border-workflow-block-border bg-workflow-block-bg p-3 pb-4 shadow-md'>
|
||||
<BubbleX className='mb-1 h-4 w-4 shrink-0 text-util-colors-teal-teal-700' />
|
||||
|
||||
@ -28,6 +28,7 @@ import type {
|
||||
} from '@/types/workflow'
|
||||
import ErrorHandleTip from '@/app/components/workflow/nodes/_base/components/error-handle/error-handle-tip'
|
||||
import { hasRetryNode } from '@/app/components/workflow/utils'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
@ -65,6 +66,7 @@ const NodePanel: FC<Props> = ({
|
||||
doSetCollapseState(state)
|
||||
}, [hideProcessDetail])
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
|
||||
const getTime = (time: number) => {
|
||||
if (time < 1)
|
||||
@ -195,7 +197,7 @@ const NodePanel: FC<Props> = ({
|
||||
<StatusContainer status='stopped'>
|
||||
{nodeInfo.error}
|
||||
<a
|
||||
href='https://docs.dify.ai/guides/workflow/error-handling/error-type'
|
||||
href={docLink('/guides/workflow/error-handling/error-type')}
|
||||
target='_blank'
|
||||
className='text-text-accent'
|
||||
>
|
||||
|
||||
@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'
|
||||
import cn from '@/utils/classnames'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
import StatusContainer from '@/app/components/workflow/run/status-container'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
type ResultProps = {
|
||||
status: string
|
||||
@ -21,6 +22,7 @@ const StatusPanel: FC<ResultProps> = ({
|
||||
exceptionCounts,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
|
||||
return (
|
||||
<StatusContainer status={status}>
|
||||
@ -134,7 +136,7 @@ const StatusPanel: FC<ResultProps> = ({
|
||||
<div className='system-xs-medium text-text-warning'>
|
||||
{error}
|
||||
<a
|
||||
href='https://docs.dify.ai/guides/workflow/error-handling/error-type'
|
||||
href={docLink('/guides/workflow/error-handling/error-type')}
|
||||
target='_blank'
|
||||
className='text-text-accent'
|
||||
>
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import {
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@ -23,13 +22,11 @@ import {
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useToastContext } from '@/app/components/base/toast'
|
||||
import { EDUCATION_VERIFYING_LOCALSTORAGE_ITEM } from '@/app/education-apply/constants'
|
||||
import { getLocaleOnClient } from '@/i18n'
|
||||
import { noop } from 'lodash-es'
|
||||
import DifyLogo from '../components/base/logo/dify-logo'
|
||||
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
const EducationApplyAge = () => {
|
||||
const { t } = useTranslation()
|
||||
const locale = getLocaleOnClient()
|
||||
const [schoolName, setSchoolName] = useState('')
|
||||
const [role, setRole] = useState('Student')
|
||||
const [ageChecked, setAgeChecked] = useState(false)
|
||||
@ -43,14 +40,7 @@ const EducationApplyAge = () => {
|
||||
const updateEducationStatus = useInvalidateEducationStatus()
|
||||
const { notify } = useToastContext()
|
||||
const router = useRouter()
|
||||
|
||||
const docLink = useMemo(() => {
|
||||
if (locale === 'zh-Hans')
|
||||
return 'https://docs.dify.ai/zh-hans/getting-started/dify-for-education'
|
||||
if (locale === 'ja-JP')
|
||||
return 'https://docs.dify.ai/ja-jp/getting-started/dify-for-education'
|
||||
return 'https://docs.dify.ai/getting-started/dify-for-education'
|
||||
}, [locale])
|
||||
const docLink = useDocLink()
|
||||
|
||||
const handleModalConfirm = () => {
|
||||
setShowModal(undefined)
|
||||
@ -167,7 +157,7 @@ const EducationApplyAge = () => {
|
||||
<div className='mb-4 mt-5 h-[1px] bg-gradient-to-r from-[rgba(16,24,40,0.08)]'></div>
|
||||
<a
|
||||
className='system-xs-regular flex items-center text-text-accent'
|
||||
href={docLink}
|
||||
href={docLink('/getting-started/dify-for-education')}
|
||||
target='_blank'
|
||||
>
|
||||
{t('education.learn')}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiExternalLinkLine,
|
||||
} from '@remixicon/react'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { getLocaleOnClient } from '@/i18n'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
export type IConfirm = {
|
||||
className?: string
|
||||
@ -30,20 +30,13 @@ function Confirm({
|
||||
email,
|
||||
}: IConfirm) {
|
||||
const { t } = useTranslation()
|
||||
const locale = getLocaleOnClient()
|
||||
const docLink = useDocLink()
|
||||
const dialogRef = useRef<HTMLDivElement>(null)
|
||||
const [isVisible, setIsVisible] = useState(isShow)
|
||||
|
||||
const docLink = useMemo(() => {
|
||||
if (locale === 'zh-Hans')
|
||||
return 'https://docs.dify.ai/zh-hans/getting-started/dify-for-education'
|
||||
if (locale === 'ja-JP')
|
||||
return 'https://docs.dify.ai/ja-jp/getting-started/dify-for-education'
|
||||
return 'https://docs.dify.ai/getting-started/dify-for-education'
|
||||
}, [locale])
|
||||
const eduDocLink = docLink('/getting-started/dify-for-education')
|
||||
|
||||
const handleClick = () => {
|
||||
window.open(docLink, '_blank', 'noopener,noreferrer')
|
||||
window.open(eduDocLink, '_blank', 'noopener,noreferrer')
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@ -106,7 +99,7 @@ function Confirm({
|
||||
<div className='flex items-center gap-1'>
|
||||
{showLink && (
|
||||
<>
|
||||
<a onClick={handleClick} href={docLink} target='_blank' className='system-xs-regular cursor-pointer text-text-accent'>{t('education.learn')}</a>
|
||||
<a onClick={handleClick} href={eduDocLink} target='_blank' className='system-xs-regular cursor-pointer text-text-accent'>{t('education.learn')}</a>
|
||||
<RiExternalLinkLine className='h-3 w-3 text-text-accent' />
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -17,6 +17,7 @@ import Button from '@/app/components/base/button'
|
||||
import { fetchInitValidateStatus, fetchSetupStatus, setup } from '@/service/common'
|
||||
import type { InitValidateStatusResponse, SetupStatusResponse } from '@/models/common'
|
||||
import useDocumentTitle from '@/hooks/use-document-title'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
const validPassword = /^(?=.*[a-zA-Z])(?=.*\d).{8,}$/
|
||||
|
||||
@ -36,6 +37,7 @@ type AccountFormValues = z.infer<typeof accountFormSchema>
|
||||
const InstallForm = () => {
|
||||
useDocumentTitle('')
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
const router = useRouter()
|
||||
const [showPassword, setShowPassword] = React.useState(false)
|
||||
const [loading, setLoading] = React.useState(true)
|
||||
@ -174,7 +176,7 @@ const InstallForm = () => {
|
||||
<Link
|
||||
className='text-text-accent'
|
||||
target='_blank' rel='noopener noreferrer'
|
||||
href={'https://docs.dify.ai/user-agreement/open-source'}
|
||||
href={docLink('/policies/open-source')}
|
||||
>{t('login.license.link')}</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
'use client'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import { useCallback, useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { useContext } from 'use-context-selector'
|
||||
@ -18,10 +19,11 @@ import Toast from '@/app/components/base/toast'
|
||||
|
||||
export default function InviteSettingsPage() {
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
const token = decodeURIComponent(searchParams.get('invite_token') as string)
|
||||
const { locale, setLocaleOnClient } = useContext(I18n)
|
||||
const { setLocaleOnClient } = useContext(I18n)
|
||||
const [name, setName] = useState('')
|
||||
const [language, setLanguage] = useState(LanguagesSupported[0])
|
||||
const [timezone, setTimezone] = useState(Intl.DateTimeFormat().resolvedOptions().timeZone || 'America/Los_Angeles')
|
||||
@ -147,7 +149,7 @@ export default function InviteSettingsPage() {
|
||||
<Link
|
||||
className='system-xs-medium text-text-accent-secondary'
|
||||
target='_blank' rel='noopener noreferrer'
|
||||
href={`https://docs.dify.ai/${language !== LanguagesSupported[1] ? 'user-agreement' : `v/${locale.toLowerCase()}/policies`}/open-source`}
|
||||
href={docLink('/policies/open-source')}
|
||||
>{t('login.license.link')}</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -12,6 +12,7 @@ import { timezones } from '@/utils/timezone'
|
||||
import { LanguagesSupported, languages } from '@/i18n/language'
|
||||
import { oneMoreStep } from '@/service/common'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
type IState = {
|
||||
formState: 'processing' | 'error' | 'success' | 'initial'
|
||||
@ -51,6 +52,7 @@ const reducer: Reducer<IState, IAction> = (state: IState, action: IAction) => {
|
||||
|
||||
const OneMoreStep = () => {
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
|
||||
@ -164,7 +166,7 @@ const OneMoreStep = () => {
|
||||
<Link
|
||||
className='system-xs-medium text-text-accent-secondary'
|
||||
target='_blank' rel='noopener noreferrer'
|
||||
href={'https://docs.dify.ai/en/policies/agreement/README'}
|
||||
href={docLink('/policies/agreement/README')}
|
||||
>{t('login.license.link')}</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -35,4 +35,17 @@ export const useGetPricingPageLanguage = () => {
|
||||
return getPricingPageLanguage(locale)
|
||||
}
|
||||
|
||||
const defaultDocBaseUrl = 'https://docs.dify.ai'
|
||||
export const useDocLink = (baseUrl?: string): ((path?: string, pathMap?: { [index: string]: string }) => string) => {
|
||||
let baseDocUrl = baseUrl || defaultDocBaseUrl
|
||||
baseDocUrl = (baseDocUrl.endsWith('/')) ? baseDocUrl.slice(0, -1) : baseDocUrl
|
||||
const { locale } = useI18N()
|
||||
const docLanguage = getDocLanguage(locale)
|
||||
return (path?: string, pathMap?: { [index: string]: string }): string => {
|
||||
const pathUrl = path || ''
|
||||
let targetPath = (pathMap) ? pathMap[locale] || pathUrl : pathUrl
|
||||
targetPath = (targetPath.startsWith('/')) ? targetPath.slice(0, -1) : targetPath
|
||||
return `${baseDocUrl}/${docLanguage}/${targetPath}`
|
||||
}
|
||||
}
|
||||
export default I18NContext
|
||||
|
||||
@ -2,7 +2,7 @@ const translation = {
|
||||
toVerified: '教育認証を取得',
|
||||
toVerifiedTip: {
|
||||
front: '現在、教育認証ステータスを取得する資格があります。以下に教育情報を入力し、認証プロセスを完了すると、Dify プロフェッショナルプランの',
|
||||
coupon: '50%割引クーポン',
|
||||
coupon: '100%割引クーポン',
|
||||
end: 'を受け取ることができます。',
|
||||
},
|
||||
currentSigned: '現在ログイン中のアカウントは',
|
||||
@ -38,9 +38,9 @@ const translation = {
|
||||
submitError: 'フォームの送信に失敗しました。しばらくしてから再度ご提出ください。',
|
||||
learn: '教育認証の取得方法はこちら',
|
||||
successTitle: 'Dify 教育認証を取得しました!',
|
||||
successContent: 'お客様のアカウントに Dify プロフェッショナルプランの 50% 割引クーポン を発行しました。有効期間は 1 年間 ですので、期限内にご利用ください。',
|
||||
successContent: 'お客様のアカウントに Dify プロフェッショナルプランの 100% 割引クーポン を発行しました。有効期間は 1 年間 ですので、期限内にご利用ください。',
|
||||
rejectTitle: 'Dify 教育認証が拒否されました',
|
||||
rejectContent: '申し訳ございませんが、このメールアドレスでは 教育認証 の資格を取得できず、Dify プロフェッショナルプランの 50%割引クーポン を受け取ることはできません。',
|
||||
rejectContent: '申し訳ございませんが、このメールアドレスでは 教育認証 の資格を取得できず、Dify プロフェッショナルプランの 100%割引クーポン を受け取ることはできません。',
|
||||
emailLabel: '現在のメールアドレス',
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user