Merge remote-tracking branch 'origin/main' into feat/17150

pull/18136/head
JF.Hsiong 1 year ago
commit b66ac8b92d

@ -6,7 +6,7 @@
本指南和 Dify 一样在不断完善中。如果有任何滞后于项目实际情况的地方,恳请谅解,我们也欢迎任何改进建议。 本指南和 Dify 一样在不断完善中。如果有任何滞后于项目实际情况的地方,恳请谅解,我们也欢迎任何改进建议。
关于许可证,请花一分钟阅读我们简短的[许可和贡献者协议](./LICENSE)。社区同时也遵循[行为准则](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md)。 关于许可证,请花一分钟阅读我们简短的[许可和贡献者协议](./LICENSE)。同时也遵循社区[行为准则](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md)。
## 开始之前 ## 开始之前

@ -9,7 +9,7 @@ class PackagingInfo(BaseSettings):
CURRENT_VERSION: str = Field( CURRENT_VERSION: str = Field(
description="Dify version", description="Dify version",
default="1.2.0", default="1.3.0",
) )
COMMIT_SHA: str = Field( COMMIT_SHA: str = Field(

@ -80,8 +80,6 @@ class ChatMessageTextApi(Resource):
@account_initialization_required @account_initialization_required
@get_app_model @get_app_model
def post(self, app_model: App): def post(self, app_model: App):
from werkzeug.exceptions import InternalServerError
try: try:
parser = reqparse.RequestParser() parser = reqparse.RequestParser()
parser.add_argument("message_id", type=str, location="json") parser.add_argument("message_id", type=str, location="json")

@ -5,6 +5,7 @@ from werkzeug.exceptions import Forbidden
from controllers.console import api from controllers.console import api
from controllers.console.wraps import account_initialization_required, setup_required from controllers.console.wraps import account_initialization_required, setup_required
from core.model_runtime.utils.encoders import jsonable_encoder from core.model_runtime.utils.encoders import jsonable_encoder
from core.plugin.manager.exc import PluginPermissionDeniedError
from libs.login import login_required from libs.login import login_required
from services.plugin.endpoint_service import EndpointService from services.plugin.endpoint_service import EndpointService
@ -28,15 +29,18 @@ class EndpointCreateApi(Resource):
settings = args["settings"] settings = args["settings"]
name = args["name"] name = args["name"]
return { try:
"success": EndpointService.create_endpoint( return {
tenant_id=user.current_tenant_id, "success": EndpointService.create_endpoint(
user_id=user.id, tenant_id=user.current_tenant_id,
plugin_unique_identifier=plugin_unique_identifier, user_id=user.id,
name=name, plugin_unique_identifier=plugin_unique_identifier,
settings=settings, name=name,
) settings=settings,
} )
}
except PluginPermissionDeniedError as e:
raise ValueError(e.description) from e
class EndpointListApi(Resource): class EndpointListApi(Resource):

@ -153,6 +153,8 @@ class MessageBasedAppGenerator(BaseAppGenerator):
query = application_generate_entity.query or "New conversation" query = application_generate_entity.query or "New conversation"
else: else:
query = next(iter(application_generate_entity.inputs.values()), "New conversation") query = next(iter(application_generate_entity.inputs.values()), "New conversation")
if isinstance(query, int):
query = str(query)
query = query or "New conversation" query = query or "New conversation"
conversation_name = (query[:20] + "") if len(query) > 20 else query conversation_name = (query[:20] + "") if len(query) > 20 else query

@ -1,7 +1,7 @@
# Written by YORKI MINAKO🤡, Edited by Xiaoyi # Written by YORKI MINAKO🤡, Edited by Xiaoyi
CONVERSATION_TITLE_PROMPT = """You need to decompose the user's input into "subject" and "intention" in order to accurately figure out what the user's input language actually is. CONVERSATION_TITLE_PROMPT = """You need to decompose the user's input into "subject" and "intention" in order to accurately figure out what the user's input language actually is.
Notice: the language type user use could be diverse, which can be English, Chinese, Italian, Español, Arabic, Japanese, French, and etc. Notice: the language type user uses could be diverse, which can be English, Chinese, Italian, Español, Arabic, Japanese, French, and etc.
MAKE SURE your output is the SAME language as the user's input! ENSURE your output is in the SAME language as the user's input!
Your output is restricted only to: (Input language) Intention + Subject(short as possible) Your output is restricted only to: (Input language) Intention + Subject(short as possible)
Your output MUST be a valid JSON. Your output MUST be a valid JSON.
@ -19,7 +19,7 @@ User Input: hi, yesterday i had some burgers.
example 2: example 2:
User Input: hello User Input: hello
{ {
"Language Type": "The user's input is written in pure English", "Language Type": "The user's input is pure English",
"Your Reasoning": "The language of my output must be pure English.", "Your Reasoning": "The language of my output must be pure English.",
"Your Output": "Greeting myself☺" "Your Output": "Greeting myself☺"
} }
@ -46,7 +46,7 @@ example 5:
User Input: why小红的年龄is老than小明 User Input: why小红的年龄is老than小明
{ {
"Language Type": "The user's input is English-Chinese mixed", "Language Type": "The user's input is English-Chinese mixed",
"Your Reasoning": "The English parts are subjective particles, the main intention is written in Chinese, besides, Chinese occupies a greater \"actual meaning\" than English, so the language of my output must be using Chinese.", "Your Reasoning": "The English parts are filler words, the main intention is written in Chinese, besides, Chinese occupies a greater \"actual meaning\" than English, so the language of my output must be using Chinese.",
"Your Output": "询问小红和小明的年龄" "Your Output": "询问小红和小明的年龄"
} }
@ -114,6 +114,13 @@ JAVASCRIPT_CODE_GENERATOR_PROMPT_TEMPLATE = (
"4. The returned object should contain at least one key-value pair.\n\n" "4. The returned object should contain at least one key-value pair.\n\n"
"5. The returned object should always be in the format: {result: ...}\n\n" "5. The returned object should always be in the format: {result: ...}\n\n"
"Example:\n" "Example:\n"
"/**\n"
" * Multiplies two numbers together.\n"
" *\n"
" * @param {number} arg1 - The first number to multiply.\n"
" * @param {number} arg2 - The second number to multiply.\n"
" * @returns {{ result: number }} The result of the multiplication.\n"
" */\n"
"function main(arg1, arg2) {\n" "function main(arg1, arg2) {\n"
" return {\n" " return {\n"
" result: arg1 * arg2\n" " result: arg1 * arg2\n"
@ -130,7 +137,7 @@ JAVASCRIPT_CODE_GENERATOR_PROMPT_TEMPLATE = (
SUGGESTED_QUESTIONS_AFTER_ANSWER_INSTRUCTION_PROMPT = ( SUGGESTED_QUESTIONS_AFTER_ANSWER_INSTRUCTION_PROMPT = (
"Please help me predict the three most likely questions that human would ask, " "Please help me predict the three most likely questions that human would ask, "
"and keeping each question under 20 characters.\n" "and keep each question under 20 characters.\n"
"MAKE SURE your output is the SAME language as the Assistant's latest response. " "MAKE SURE your output is the SAME language as the Assistant's latest response. "
"The output must be an array in JSON format following the specified schema:\n" "The output must be an array in JSON format following the specified schema:\n"
'["question1","question2","question3"]\n' '["question1","question2","question3"]\n'
@ -157,9 +164,9 @@ Here is a task description for which I would like you to create a high-quality p
</task_description> </task_description>
Based on task description, please create a well-structured prompt template that another AI could use to consistently complete the task. The prompt template should include: Based on task description, please create a well-structured prompt template that another AI could use to consistently complete the task. The prompt template should include:
- Do not include <input> or <output> section and variables in the prompt, assume user will add them at their own will. - Do not include <input> or <output> section and variables in the prompt, assume user will add them at their own will.
- Clear instructions for the AI that will be using this prompt, demarcated with <instructions> tags. The instructions should provide step-by-step directions on how to complete the task using the input variables. Also Specifies in the instructions that the output should not contain any xml tag. - Clear instructions for the AI that will be using this prompt, demarcated with <instruction> tags. The instructions should provide step-by-step directions on how to complete the task using the input variables. Also Specifies in the instructions that the output should not contain any xml tag.
- Relevant examples if needed to clarify the task further, demarcated with <example> tags. Do not include variables in the prompt. Give three pairs of input and output examples. - Relevant examples if needed to clarify the task further, demarcated with <example> tags. Do not include variables in the prompt. Give three pairs of input and output examples.
- Include other relevant sections demarcated with appropriate XML tags like <examples>, <instructions>. - Include other relevant sections demarcated with appropriate XML tags like <examples>, <instruction>.
- Use the same language as task description. - Use the same language as task description.
- Output in ``` xml ``` and start with <instruction> - Output in ``` xml ``` and start with <instruction>
Please generate the full prompt template with at least 300 words and output only the prompt template. Please generate the full prompt template with at least 300 words and output only the prompt template.
@ -172,7 +179,7 @@ Here is a task description for which I would like you to create a high-quality p
</task_description> </task_description>
Based on task description, please create a well-structured prompt template that another AI could use to consistently complete the task. The prompt template should include: Based on task description, please create a well-structured prompt template that another AI could use to consistently complete the task. The prompt template should include:
- Descriptive variable names surrounded by {{ }} (two curly brackets) to indicate where the actual values will be substituted in. Choose variable names that clearly indicate the type of value expected. Variable names have to be composed of number, english alphabets and underline and nothing else. - Descriptive variable names surrounded by {{ }} (two curly brackets) to indicate where the actual values will be substituted in. Choose variable names that clearly indicate the type of value expected. Variable names have to be composed of number, english alphabets and underline and nothing else.
- Clear instructions for the AI that will be using this prompt, demarcated with <instructions> tags. The instructions should provide step-by-step directions on how to complete the task using the input variables. Also Specifies in the instructions that the output should not contain any xml tag. - Clear instructions for the AI that will be using this prompt, demarcated with <instruction> tags. The instructions should provide step-by-step directions on how to complete the task using the input variables. Also Specifies in the instructions that the output should not contain any xml tag.
- Relevant examples if needed to clarify the task further, demarcated with <example> tags. Do not use curly brackets any other than in <instruction> section. - Relevant examples if needed to clarify the task further, demarcated with <example> tags. Do not use curly brackets any other than in <instruction> section.
- Any other relevant sections demarcated with appropriate XML tags like <input>, <output>, etc. - Any other relevant sections demarcated with appropriate XML tags like <input>, <output>, etc.
- Use the same language as task description. - Use the same language as task description.
@ -291,32 +298,30 @@ Your task is to convert simple user descriptions into properly formatted JSON Sc
{ {
"type": "object", "type": "object",
"properties": { "properties": {
"properties": { "songs": {
"songs": { "type": "array",
"type": "array", "items": {
"items": { "type": "object",
"type": "object", "properties": {
"properties": { "name": {
"name": { "type": "string"
"type": "string" },
}, "id": {
"id": { "type": "string"
"type": "string" },
}, "duration": {
"duration": { "type": "string"
"type": "string"
},
"aritst": {
"type": "string"
}
}, },
"required": [ "aritst": {
"name", "type": "string"
"id", }
"duration", },
"aritst" "required": [
] "name",
} "id",
"duration",
"aritst"
]
} }
} }
}, },

@ -3,8 +3,8 @@ import re
import uuid import uuid
from collections.abc import Mapping from collections.abc import Mapping
from datetime import datetime from datetime import datetime
from enum import Enum from enum import Enum, StrEnum
from typing import TYPE_CHECKING, Optional from typing import TYPE_CHECKING, Any, Literal, Optional, cast
from core.plugin.entities.plugin import GenericProviderID from core.plugin.entities.plugin import GenericProviderID
from core.tools.entities.tool_entities import ToolProviderType from core.tools.entities.tool_entities import ToolProviderType
@ -13,9 +13,6 @@ from services.plugin.plugin_service import PluginService
if TYPE_CHECKING: if TYPE_CHECKING:
from models.workflow import Workflow from models.workflow import Workflow
from enum import StrEnum
from typing import TYPE_CHECKING, Any, Literal, cast
import sqlalchemy as sa import sqlalchemy as sa
from flask import request from flask import request
from flask_login import UserMixin # type: ignore from flask_login import UserMixin # type: ignore

@ -1,14 +1,12 @@
import json import json
from collections.abc import Mapping, Sequence from collections.abc import Mapping, Sequence
from datetime import UTC, datetime from datetime import UTC, datetime
from enum import Enum from enum import Enum, StrEnum
from typing import TYPE_CHECKING, Any, Optional, Self, Union from typing import TYPE_CHECKING, Any, Optional, Self, Union
from uuid import uuid4 from uuid import uuid4
if TYPE_CHECKING: if TYPE_CHECKING:
from models.model import AppMode from models.model import AppMode
from enum import StrEnum
from typing import TYPE_CHECKING
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy import Index, PrimaryKeyConstraint, func from sqlalchemy import Index, PrimaryKeyConstraint, func

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

@ -40,7 +40,7 @@ IMPORT_INFO_REDIS_KEY_PREFIX = "app_import_info:"
CHECK_DEPENDENCIES_REDIS_KEY_PREFIX = "app_check_dependencies:" CHECK_DEPENDENCIES_REDIS_KEY_PREFIX = "app_check_dependencies:"
IMPORT_INFO_REDIS_EXPIRY = 10 * 60 # 10 minutes IMPORT_INFO_REDIS_EXPIRY = 10 * 60 # 10 minutes
DSL_MAX_SIZE = 10 * 1024 * 1024 # 10MB DSL_MAX_SIZE = 10 * 1024 * 1024 # 10MB
CURRENT_DSL_VERSION = "0.1.5" CURRENT_DSL_VERSION = "0.2.0"
class ImportMode(StrEnum): class ImportMode(StrEnum):

@ -1148,7 +1148,7 @@ wheels = [
[[package]] [[package]]
name = "dify-api" name = "dify-api"
version = "1.2.0" version = "1.3.0"
source = { virtual = "." } source = { virtual = "." }
dependencies = [ dependencies = [
{ name = "authlib" }, { name = "authlib" },

@ -2,7 +2,7 @@ x-shared-env: &shared-api-worker-env
services: services:
# API service # API service
api: api:
image: langgenius/dify-api:1.2.0 image: langgenius/dify-api:1.3.0
restart: always restart: always
environment: environment:
# Use the shared environment variables. # Use the shared environment variables.
@ -31,7 +31,7 @@ services:
# worker service # worker service
# The Celery worker for processing the queue. # The Celery worker for processing the queue.
worker: worker:
image: langgenius/dify-api:1.2.0 image: langgenius/dify-api:1.3.0
restart: always restart: always
environment: environment:
# Use the shared environment variables. # Use the shared environment variables.
@ -57,7 +57,7 @@ services:
# Frontend web application. # Frontend web application.
web: web:
image: langgenius/dify-web:1.2.0 image: langgenius/dify-web:1.3.0
restart: always restart: always
environment: environment:
CONSOLE_API_URL: ${CONSOLE_API_URL:-} CONSOLE_API_URL: ${CONSOLE_API_URL:-}
@ -142,7 +142,7 @@ services:
# plugin daemon # plugin daemon
plugin_daemon: plugin_daemon:
image: langgenius/dify-plugin-daemon:0.0.7-local image: langgenius/dify-plugin-daemon:0.0.9-local
restart: always restart: always
environment: environment:
# Use the shared environment variables. # Use the shared environment variables.

@ -71,7 +71,7 @@ services:
# plugin daemon # plugin daemon
plugin_daemon: plugin_daemon:
image: langgenius/dify-plugin-daemon:0.0.7-local image: langgenius/dify-plugin-daemon:0.0.9-local
restart: always restart: always
env_file: env_file:
- ./middleware.env - ./middleware.env

@ -479,7 +479,7 @@ x-shared-env: &shared-api-worker-env
services: services:
# API service # API service
api: api:
image: langgenius/dify-api:1.2.0 image: langgenius/dify-api:1.3.0
restart: always restart: always
environment: environment:
# Use the shared environment variables. # Use the shared environment variables.
@ -508,7 +508,7 @@ services:
# worker service # worker service
# The Celery worker for processing the queue. # The Celery worker for processing the queue.
worker: worker:
image: langgenius/dify-api:1.2.0 image: langgenius/dify-api:1.3.0
restart: always restart: always
environment: environment:
# Use the shared environment variables. # Use the shared environment variables.
@ -534,7 +534,7 @@ services:
# Frontend web application. # Frontend web application.
web: web:
image: langgenius/dify-web:1.2.0 image: langgenius/dify-web:1.3.0
restart: always restart: always
environment: environment:
CONSOLE_API_URL: ${CONSOLE_API_URL:-} CONSOLE_API_URL: ${CONSOLE_API_URL:-}
@ -619,7 +619,7 @@ services:
# plugin daemon # plugin daemon
plugin_daemon: plugin_daemon:
image: langgenius/dify-plugin-daemon:0.0.7-local image: langgenius/dify-plugin-daemon:0.0.9-local
restart: always restart: always
environment: environment:
# Use the shared environment variables. # Use the shared environment variables.

@ -0,0 +1 @@
/vendor

@ -9,6 +9,21 @@ This is the PHP SDK for the Dify API, which allows you to easily integrate Dify
## Usage ## Usage
If you want to try the example, you can run `composer install` in this directory.
In exist project, copy the `dify-client.php` to you project, and merge the following to your `composer.json` file, then run `composer install && composer dump-autoload` to install. Guzzle does not require 7.9, other versions have not been tested, but you can try.
```json
{
"require": {
"guzzlehttp/guzzle": "^7.9"
},
"autoload": {
"files": ["path/to/dify-client.php"]
}
}
```
After installing the SDK, you can use it in your project like this: After installing the SDK, you can use it in your project like this:
```php ```php
@ -16,10 +31,6 @@ After installing the SDK, you can use it in your project like this:
require 'vendor/autoload.php'; require 'vendor/autoload.php';
use YourVendorName\DifyPHP\DifyClient;
use YourVendorName\DifyPHP\CompletionClient;
use YourVendorName\DifyPHP\ChatClient;
$apiKey = 'your-api-key-here'; $apiKey = 'your-api-key-here';
$difyClient = new DifyClient($apiKey); $difyClient = new DifyClient($apiKey);

@ -0,0 +1,9 @@
{
"require": {
"php": ">=7.2",
"guzzlehttp/guzzle": "^7.9"
},
"autoload": {
"files": ["dify-client.php"]
}
}

@ -0,0 +1,663 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "7827c548fdcc7e87cb0ae341dd2c6b1b",
"packages": [
{
"name": "guzzlehttp/guzzle",
"version": "7.9.2",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "d281ed313b989f213357e3be1a179f02196ac99b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b",
"reference": "d281ed313b989f213357e3be1a179f02196ac99b",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"ext-json": "*",
"guzzlehttp/promises": "^1.5.3 || ^2.0.3",
"guzzlehttp/psr7": "^2.7.0",
"php": "^7.2.5 || ^8.0",
"psr/http-client": "^1.0",
"symfony/deprecation-contracts": "^2.2 || ^3.0"
},
"provide": {
"psr/http-client-implementation": "1.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"ext-curl": "*",
"guzzle/client-integration-tests": "3.0.2",
"php-http/message-factory": "^1.1",
"phpunit/phpunit": "^8.5.39 || ^9.6.20",
"psr/log": "^1.1 || ^2.0 || ^3.0"
},
"suggest": {
"ext-curl": "Required for CURL handler support",
"ext-intl": "Required for Internationalized Domain Name (IDN) support",
"psr/log": "Required for using the Log middleware"
},
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": false
}
},
"autoload": {
"files": [
"src/functions_include.php"
],
"psr-4": {
"GuzzleHttp\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
},
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
},
{
"name": "Jeremy Lindblom",
"email": "jeremeamia@gmail.com",
"homepage": "https://github.com/jeremeamia"
},
{
"name": "George Mponos",
"email": "gmponos@gmail.com",
"homepage": "https://github.com/gmponos"
},
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/Nyholm"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com",
"homepage": "https://github.com/sagikazarmark"
},
{
"name": "Tobias Schultze",
"email": "webmaster@tubo-world.de",
"homepage": "https://github.com/Tobion"
}
],
"description": "Guzzle is a PHP HTTP client library",
"keywords": [
"client",
"curl",
"framework",
"http",
"http client",
"psr-18",
"psr-7",
"rest",
"web service"
],
"support": {
"issues": "https://github.com/guzzle/guzzle/issues",
"source": "https://github.com/guzzle/guzzle/tree/7.9.2"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://github.com/Nyholm",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
"type": "tidelift"
}
],
"time": "2024-07-24T11:22:20+00:00"
},
{
"name": "guzzlehttp/promises",
"version": "2.2.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
"reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c",
"reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": "^7.2.5 || ^8.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"phpunit/phpunit": "^8.5.39 || ^9.6.20"
},
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": false
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\Promise\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
},
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
},
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/Nyholm"
},
{
"name": "Tobias Schultze",
"email": "webmaster@tubo-world.de",
"homepage": "https://github.com/Tobion"
}
],
"description": "Guzzle promises library",
"keywords": [
"promise"
],
"support": {
"issues": "https://github.com/guzzle/promises/issues",
"source": "https://github.com/guzzle/promises/tree/2.2.0"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://github.com/Nyholm",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
"type": "tidelift"
}
],
"time": "2025-03-27T13:27:01+00:00"
},
{
"name": "guzzlehttp/psr7",
"version": "2.7.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16",
"reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": "^7.2.5 || ^8.0",
"psr/http-factory": "^1.0",
"psr/http-message": "^1.1 || ^2.0",
"ralouphie/getallheaders": "^3.0"
},
"provide": {
"psr/http-factory-implementation": "1.0",
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"http-interop/http-factory-tests": "0.9.0",
"phpunit/phpunit": "^8.5.39 || ^9.6.20"
},
"suggest": {
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
},
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": false
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\Psr7\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
},
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
},
{
"name": "George Mponos",
"email": "gmponos@gmail.com",
"homepage": "https://github.com/gmponos"
},
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/Nyholm"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com",
"homepage": "https://github.com/sagikazarmark"
},
{
"name": "Tobias Schultze",
"email": "webmaster@tubo-world.de",
"homepage": "https://github.com/Tobion"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com",
"homepage": "https://sagikazarmark.hu"
}
],
"description": "PSR-7 message implementation that also provides common utility methods",
"keywords": [
"http",
"message",
"psr-7",
"request",
"response",
"stream",
"uri",
"url"
],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
"source": "https://github.com/guzzle/psr7/tree/2.7.1"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://github.com/Nyholm",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
"type": "tidelift"
}
],
"time": "2025-03-27T12:30:47+00:00"
},
{
"name": "psr/http-client",
"version": "1.0.3",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-client.git",
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": "^7.0 || ^8.0",
"psr/http-message": "^1.0 || ^2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Client\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for HTTP clients",
"homepage": "https://github.com/php-fig/http-client",
"keywords": [
"http",
"http-client",
"psr",
"psr-18"
],
"support": {
"source": "https://github.com/php-fig/http-client"
},
"time": "2023-09-23T14:17:50+00:00"
},
{
"name": "psr/http-factory",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-factory.git",
"reference": "e616d01114759c4c489f93b099585439f795fe35"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35",
"reference": "e616d01114759c4c489f93b099585439f795fe35",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=7.0.0",
"psr/http-message": "^1.0 || ^2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interfaces for PSR-7 HTTP message factories",
"keywords": [
"factory",
"http",
"message",
"psr",
"psr-17",
"psr-7",
"request",
"response"
],
"support": {
"source": "https://github.com/php-fig/http-factory/tree/1.0.2"
},
"time": "2023-04-10T20:10:41+00:00"
},
{
"name": "psr/http-message",
"version": "2.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": "^7.2 || ^8.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
],
"support": {
"source": "https://github.com/php-fig/http-message/tree/2.0"
},
"time": "2023-04-04T09:54:51+00:00"
},
{
"name": "ralouphie/getallheaders",
"version": "3.0.3",
"source": {
"type": "git",
"url": "https://github.com/ralouphie/getallheaders.git",
"reference": "120b605dfeb996808c31b6477290a714d356e822"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
"reference": "120b605dfeb996808c31b6477290a714d356e822",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=5.6"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.1",
"phpunit/phpunit": "^5 || ^6.5"
},
"type": "library",
"autoload": {
"files": [
"src/getallheaders.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ralph Khattar",
"email": "ralph.khattar@gmail.com"
}
],
"description": "A polyfill for getallheaders.",
"support": {
"issues": "https://github.com/ralouphie/getallheaders/issues",
"source": "https://github.com/ralouphie/getallheaders/tree/develop"
},
"time": "2019-03-08T08:55:37+00:00"
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.5.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
"reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=8.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {
"files": [
"function.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-09-25T14:20:29+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.6.0"
}

@ -1,7 +1,5 @@
<?php <?php
require 'vendor/autoload.php';
use GuzzleHttp\Client; use GuzzleHttp\Client;
class DifyClient { class DifyClient {
@ -11,7 +9,7 @@ class DifyClient {
public function __construct($api_key, $base_url = null) { public function __construct($api_key, $base_url = null) {
$this->api_key = $api_key; $this->api_key = $api_key;
$this->base_url = $base_url ?? "https://api.dify.ai/v1/"; $this->base_url = $base_url ?? 'https://api.dify.ai/v1/';
$this->client = new Client([ $this->client = new Client([
'base_uri' => $this->base_url, 'base_uri' => $this->base_url,
'headers' => [ 'headers' => [
@ -19,13 +17,6 @@ class DifyClient {
'Content-Type' => 'application/json', 'Content-Type' => 'application/json',
], ],
]); ]);
$this->file_client = new Client([
'base_uri' => $this->base_url,
'headers' => [
'Authorization' => 'Bearer ' . $this->api_key,
'Content-Type' => 'multipart/form-data',
],
]);
} }
protected function send_request($method, $endpoint, $data = null, $params = null, $stream = false) { protected function send_request($method, $endpoint, $data = null, $params = null, $stream = false) {
@ -58,7 +49,7 @@ class DifyClient {
'multipart' => $this->prepareMultipart($data, $files) 'multipart' => $this->prepareMultipart($data, $files)
]; ];
return $this->file_client->request('POST', 'files/upload', $options); return $this->client->request('POST', 'files/upload', $options);
} }
protected function prepareMultipart($data, $files) { protected function prepareMultipart($data, $files) {
@ -132,7 +123,7 @@ class ChatClient extends DifyClient {
public function get_suggestions($message_id, $user) { public function get_suggestions($message_id, $user) {
$params = [ $params = [
'user' => $user 'user' => $user
] ];
return $this->send_request('GET', "messages/{$message_id}/suggested", null, $params); return $this->send_request('GET', "messages/{$message_id}/suggested", null, $params);
} }
@ -188,10 +179,9 @@ class ChatClient extends DifyClient {
'user' => $user, 'user' => $user,
]; ];
$options = [ $options = [
'multipart' => $this->prepareMultipart($data, $files) 'multipart' => $this->prepareMultipart($data, $audio_file)
]; ];
return $this->file_client->request('POST', 'audio-to-text', $options); return $this->client->request('POST', 'audio-to-text', $options);
} }
} }

@ -14,6 +14,7 @@ import Loading from '@/app/components/base/loading'
import Badge from '@/app/components/base/badge' import Badge from '@/app/components/base/badge'
import { useKnowledge } from '@/hooks/use-knowledge' import { useKnowledge } from '@/hooks/use-knowledge'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
import { basePath } from '@/utils/var'
export type ISelectDataSetProps = { export type ISelectDataSetProps = {
isShow: boolean isShow: boolean
@ -111,7 +112,7 @@ const SelectDataSet: FC<ISelectDataSetProps> = ({
}} }}
> >
<span className='text-text-tertiary'>{t('appDebug.feature.dataSet.noDataSet')}</span> <span className='text-text-tertiary'>{t('appDebug.feature.dataSet.noDataSet')}</span>
<Link href="/datasets/create" className='font-normal text-text-accent'>{t('appDebug.feature.dataSet.toCreate')}</Link> <Link href={`${basePath}/datasets/create`} className='font-normal text-text-accent'>{t('appDebug.feature.dataSet.toCreate')}</Link>
</div> </div>
)} )}

@ -29,7 +29,7 @@ const OPTION_MAP = {
iframe: { iframe: {
getContent: (url: string, token: string) => getContent: (url: string, token: string) =>
`<iframe `<iframe
src="${url}${basePath}/chat/${token}" src="${url}${basePath}/chatbot/${token}"
style="width: 100%; height: 100%; min-height: 700px" style="width: 100%; height: 100%; min-height: 700px"
frameborder="0" frameborder="0"
allow="microphone"> allow="microphone">
@ -48,6 +48,7 @@ const OPTION_MAP = {
: ''}, : ''},
systemVariables: { systemVariables: {
// user_id: 'YOU CAN DEFINE USER ID HERE', // user_id: 'YOU CAN DEFINE USER ID HERE',
// conversation_id: 'YOU CAN DEFINE CONVERSATION ID HERE, IT MUST BE A VALID UUID',
}, },
} }
</script> </script>

@ -39,6 +39,7 @@ export type EmbeddedChatbotContextValue = {
chatShouldReloadKey: string chatShouldReloadKey: string
isMobile: boolean isMobile: boolean
isInstalledApp: boolean isInstalledApp: boolean
allowResetChat: boolean
appId?: string appId?: string
handleFeedback: (messageId: string, feedback: Feedback) => void handleFeedback: (messageId: string, feedback: Feedback) => void
currentChatInstanceRef: RefObject<{ handleStop: () => void }> currentChatInstanceRef: RefObject<{ handleStop: () => void }>
@ -67,6 +68,7 @@ export const EmbeddedChatbotContext = createContext<EmbeddedChatbotContextValue>
chatShouldReloadKey: '', chatShouldReloadKey: '',
isMobile: false, isMobile: false,
isInstalledApp: false, isInstalledApp: false,
allowResetChat: true,
handleFeedback: noop, handleFeedback: noop,
currentChatInstanceRef: { current: { handleStop: noop } }, currentChatInstanceRef: { current: { handleStop: noop } },
clearChatList: false, clearChatList: false,

@ -16,6 +16,7 @@ import cn from '@/utils/classnames'
export type IHeaderProps = { export type IHeaderProps = {
isMobile?: boolean isMobile?: boolean
allowResetChat?: boolean
customerIcon?: React.ReactNode customerIcon?: React.ReactNode
title: string title: string
theme?: Theme theme?: Theme
@ -23,6 +24,7 @@ export type IHeaderProps = {
} }
const Header: FC<IHeaderProps> = ({ const Header: FC<IHeaderProps> = ({
isMobile, isMobile,
allowResetChat,
customerIcon, customerIcon,
title, title,
theme, theme,
@ -57,7 +59,7 @@ const Header: FC<IHeaderProps> = ({
{currentConversationId && ( {currentConversationId && (
<Divider type='vertical' className='h-3.5' /> <Divider type='vertical' className='h-3.5' />
)} )}
{currentConversationId && ( {currentConversationId && allowResetChat && (
<Tooltip <Tooltip
popupContent={t('share.chat.resetChat')} popupContent={t('share.chat.resetChat')}
> >
@ -89,7 +91,7 @@ const Header: FC<IHeaderProps> = ({
</div> </div>
</div> </div>
<div className='flex items-center gap-1'> <div className='flex items-center gap-1'>
{currentConversationId && ( {currentConversationId && allowResetChat && (
<Tooltip <Tooltip
popupContent={t('share.chat.resetChat')} popupContent={t('share.chat.resetChat')}
> >

@ -73,9 +73,11 @@ export const useEmbeddedChatbot = () => {
const appId = useMemo(() => appData?.app_id, [appData]) const appId = useMemo(() => appData?.app_id, [appData])
const [userId, setUserId] = useState<string>() const [userId, setUserId] = useState<string>()
const [conversationId, setConversationId] = useState<string>()
useEffect(() => { useEffect(() => {
getProcessedSystemVariablesFromUrlParams().then(({ user_id }) => { getProcessedSystemVariablesFromUrlParams().then(({ user_id, conversation_id }) => {
setUserId(user_id) setUserId(user_id)
setConversationId(conversation_id)
}) })
}, []) }, [])
@ -109,7 +111,9 @@ export const useEmbeddedChatbot = () => {
const [conversationIdInfo, setConversationIdInfo] = useLocalStorageState<Record<string, Record<string, string>>>(CONVERSATION_ID_INFO, { const [conversationIdInfo, setConversationIdInfo] = useLocalStorageState<Record<string, Record<string, string>>>(CONVERSATION_ID_INFO, {
defaultValue: {}, defaultValue: {},
}) })
const currentConversationId = useMemo(() => conversationIdInfo?.[appId || '']?.[userId || 'DEFAULT'] || '', [appId, conversationIdInfo, userId]) const allowResetChat = !conversationId
const currentConversationId = useMemo(() => conversationIdInfo?.[appId || '']?.[userId || 'DEFAULT'] || conversationId || '',
[appId, conversationIdInfo, userId, conversationId])
const handleConversationIdInfoChange = useCallback((changeConversationId: string) => { const handleConversationIdInfoChange = useCallback((changeConversationId: string) => {
if (appId) { if (appId) {
let prevValue = conversationIdInfo?.[appId || ''] let prevValue = conversationIdInfo?.[appId || '']
@ -362,6 +366,7 @@ export const useEmbeddedChatbot = () => {
appInfoError, appInfoError,
appInfoLoading, appInfoLoading,
isInstalledApp, isInstalledApp,
allowResetChat,
appId, appId,
currentConversationId, currentConversationId,
currentConversationItem, currentConversationItem,

@ -25,6 +25,7 @@ import cn from '@/utils/classnames'
const Chatbot = () => { const Chatbot = () => {
const { const {
isMobile, isMobile,
allowResetChat,
appInfoError, appInfoError,
appInfoLoading, appInfoLoading,
appData, appData,
@ -90,6 +91,7 @@ const Chatbot = () => {
> >
<Header <Header
isMobile={isMobile} isMobile={isMobile}
allowResetChat={allowResetChat}
title={site?.title || ''} title={site?.title || ''}
customerIcon={isDify() ? difyIcon : ''} customerIcon={isDify() ? difyIcon : ''}
theme={themeBuilder?.theme} theme={themeBuilder?.theme}
@ -153,6 +155,7 @@ const EmbeddedChatbotWrapper = () => {
handleNewConversationCompleted, handleNewConversationCompleted,
chatShouldReloadKey, chatShouldReloadKey,
isInstalledApp, isInstalledApp,
allowResetChat,
appId, appId,
handleFeedback, handleFeedback,
currentChatInstanceRef, currentChatInstanceRef,
@ -187,6 +190,7 @@ const EmbeddedChatbotWrapper = () => {
chatShouldReloadKey, chatShouldReloadKey,
isMobile, isMobile,
isInstalledApp, isInstalledApp,
allowResetChat,
appId, appId,
handleFeedback, handleFeedback,
currentChatInstanceRef, currentChatInstanceRef,

@ -1,4 +1,5 @@
import type { FC } from 'react' import type { FC } from 'react'
import { basePath } from '@/utils/var'
type LogoEmbeddedChatAvatarProps = { type LogoEmbeddedChatAvatarProps = {
className?: string className?: string
@ -8,7 +9,7 @@ const LogoEmbeddedChatAvatar: FC<LogoEmbeddedChatAvatarProps> = ({
}) => { }) => {
return ( return (
<img <img
src='/logo/logo-embedded-chat-avatar.png' src={`${basePath}/logo/logo-embedded-chat-avatar.png`}
className={`block h-10 w-10 ${className}`} className={`block h-10 w-10 ${className}`}
alt='logo' alt='logo'
/> />

@ -68,7 +68,7 @@ const Toast = ({
</div> </div>
<div className={`flex py-1 ${size === 'md' ? 'px-1' : 'px-0.5'} grow flex-col items-start gap-1`}> <div className={`flex py-1 ${size === 'md' ? 'px-1' : 'px-0.5'} grow flex-col items-start gap-1`}>
<div className='flex items-center gap-1'> <div className='flex items-center gap-1'>
<div className='system-sm-semibold text-text-primary'>{message}</div> <div className='system-sm-semibold text-text-primary [word-break:break-word]'>{message}</div>
{customComponent} {customComponent}
</div> </div>
{children && <div className='system-xs-regular text-text-secondary'> {children && <div className='system-xs-regular text-text-secondary'>

@ -87,7 +87,7 @@ const Doc = ({ appDetail }: IDocProps) => {
<div className={`fixed right-8 top-32 z-10 transition-all ${isTocExpanded ? 'w-64' : 'w-10'}`}> <div className={`fixed right-8 top-32 z-10 transition-all ${isTocExpanded ? 'w-64' : 'w-10'}`}>
{isTocExpanded {isTocExpanded
? ( ? (
<nav className="toc w-full rounded-lg bg-components-panel-bg p-4 shadow-md"> <nav className="toc max-h-[calc(100vh-150px)] w-full overflow-y-auto rounded-lg bg-components-panel-bg p-4 shadow-md">
<div className="mb-4 flex items-center justify-between"> <div className="mb-4 flex items-center justify-between">
<h3 className="text-lg font-semibold text-text-primary">{t('appApi.develop.toc')}</h3> <h3 className="text-lg font-semibold text-text-primary">{t('appApi.develop.toc')}</h3>
<button <button

@ -3,7 +3,6 @@ import type {
Model, Model,
ModelProvider, ModelProvider,
} from '../declarations' } from '../declarations'
import { basePath } from '@/utils/var'
import { useLanguage } from '../hooks' import { useLanguage } from '../hooks'
import { Group } from '@/app/components/base/icons/src/vender/other' import { Group } from '@/app/components/base/icons/src/vender/other'
import { OpenaiBlue, OpenaiViolet } from '@/app/components/base/icons/src/public/llm' import { OpenaiBlue, OpenaiViolet } from '@/app/components/base/icons/src/public/llm'
@ -31,7 +30,7 @@ const ModelIcon: FC<ModelIconProps> = ({
if (provider?.icon_small) { if (provider?.icon_small) {
return ( return (
<div className={cn('flex h-5 w-5 items-center justify-center', isDeprecated && 'opacity-50', className)}> <div className={cn('flex h-5 w-5 items-center justify-center', isDeprecated && 'opacity-50', className)}>
<img alt='model-icon' src={basePath + renderI18nObject(provider.icon_small, language)}/> <img alt='model-icon' src={renderI18nObject(provider.icon_small, language)}/>
</div> </div>
) )
} }

@ -39,6 +39,7 @@ const Input: FC<InputProps> = ({
return ( return (
<div className='relative'> <div className='relative'>
<input <input
autoComplete="new-password"
tabIndex={0} tabIndex={0}
className={` className={`
block h-8 w-full appearance-none rounded-lg border border-transparent bg-components-input-bg-normal px-3 text-sm block h-8 w-full appearance-none rounded-lg border border-transparent bg-components-input-bg-normal px-3 text-sm

@ -14,6 +14,7 @@ import Nav from '../nav'
import type { NavItem } from '../nav/nav-selector' import type { NavItem } from '../nav/nav-selector'
import { fetchDatasetDetail, fetchDatasets } from '@/service/datasets' import { fetchDatasetDetail, fetchDatasets } from '@/service/datasets'
import type { DataSetListResponse } from '@/models/datasets' import type { DataSetListResponse } from '@/models/datasets'
import { basePath } from '@/utils/var'
const getKey = (pageIndex: number, previousPageData: DataSetListResponse) => { const getKey = (pageIndex: number, previousPageData: DataSetListResponse) => {
if (!pageIndex || previousPageData.has_more) if (!pageIndex || previousPageData.has_more)
@ -56,7 +57,7 @@ const DatasetNav = () => {
icon_background: dataset.icon_background, icon_background: dataset.icon_background,
})) as NavItem[]} })) as NavItem[]}
createText={t('common.menus.newDataset')} createText={t('common.menus.newDataset')}
onCreate={() => router.push('/datasets/create')} onCreate={() => router.push(`${basePath}/datasets/create`)}
onLoadmore={handleLoadmore} onLoadmore={handleLoadmore}
/> />
) )

@ -3,9 +3,15 @@ import ChatVariableButton from '@/app/components/workflow/header/chat-variable-b
import { import {
useNodesReadOnly, useNodesReadOnly,
} from '@/app/components/workflow/hooks' } from '@/app/components/workflow/hooks'
import { useIsChatMode } from '../../hooks'
const ChatVariableTrigger = () => { const ChatVariableTrigger = () => {
const { nodesReadOnly } = useNodesReadOnly() const { nodesReadOnly } = useNodesReadOnly()
const isChatMode = useIsChatMode()
if (!isChatMode)
return null
return <ChatVariableButton disabled={nodesReadOnly} /> return <ChatVariableButton disabled={nodesReadOnly} />
} }
export default memo(ChatVariableTrigger) export default memo(ChatVariableTrigger)

@ -74,7 +74,7 @@ const WorkflowPanelOnRight = () => {
) )
} }
{ {
showChatVariablePanel && ( showChatVariablePanel && isChatMode && (
<ChatVariablePanel /> <ChatVariablePanel />
) )
} }

@ -47,8 +47,14 @@ const VariableModal = ({
return return
if (!value) if (!value)
return notify({ type: 'error', message: 'value can not be empty' }) return notify({ type: 'error', message: 'value can not be empty' })
if (!env && envList.some(env => env.name === name))
// Add check for duplicate name when editing
if (env && env.name !== name && envList.some(e => e.name === name))
return notify({ type: 'error', message: 'name is existed' })
// Original check for create new variable
if (!env && envList.some(e => e.name === name))
return notify({ type: 'error', message: 'name is existed' }) return notify({ type: 'error', message: 'name is existed' })
onSave({ onSave({
id: env ? env.id : uuid4(), id: env ? env.id : uuid4(),
value_type: type, value_type: type,

@ -16,6 +16,7 @@ import Button from '@/app/components/base/button'
import { fetchInitValidateStatus, fetchSetupStatus, setup } from '@/service/common' import { fetchInitValidateStatus, fetchSetupStatus, setup } from '@/service/common'
import type { InitValidateStatusResponse, SetupStatusResponse } from '@/models/common' import type { InitValidateStatusResponse, SetupStatusResponse } from '@/models/common'
import { basePath } from '@/utils/var'
const validPassword = /^(?=.*[a-zA-Z])(?=.*\d).{8,}$/ const validPassword = /^(?=.*[a-zA-Z])(?=.*\d).{8,}$/
@ -80,12 +81,12 @@ const InstallForm = () => {
fetchSetupStatus().then((res: SetupStatusResponse) => { fetchSetupStatus().then((res: SetupStatusResponse) => {
if (res.step === 'finished') { if (res.step === 'finished') {
localStorage.setItem('setup_status', 'finished') localStorage.setItem('setup_status', 'finished')
router.push('/signin') router.push(`${basePath}/signin`)
} }
else { else {
fetchInitValidateStatus().then((res: InitValidateStatusResponse) => { fetchInitValidateStatus().then((res: InitValidateStatusResponse) => {
if (res.status === 'not_started') if (res.status === 'not_started')
router.push('/init') router.push(`${basePath}/init`)
}) })
} }
setLoading(false) setLoading(false)

@ -54,6 +54,9 @@ const LocaleLayout = async ({
data-public-indexing-max-segmentation-tokens-length={process.env.NEXT_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH} data-public-indexing-max-segmentation-tokens-length={process.env.NEXT_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH}
data-public-loop-node-max-count={process.env.NEXT_PUBLIC_LOOP_NODE_MAX_COUNT} data-public-loop-node-max-count={process.env.NEXT_PUBLIC_LOOP_NODE_MAX_COUNT}
data-public-max-iterations-num={process.env.NEXT_PUBLIC_MAX_ITERATIONS_NUM} data-public-max-iterations-num={process.env.NEXT_PUBLIC_MAX_ITERATIONS_NUM}
data-public-enable-website-jinareader={process.env.NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER}
data-public-enable-website-firecrawl={process.env.NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL}
data-public-enable-website-watercrawl={process.env.NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL}
> >
<BrowserInitor> <BrowserInitor>
<SentryInitor> <SentryInitor>

@ -306,12 +306,12 @@ export const MAX_ITERATIONS_NUM = maxIterationsNum
export const ENABLE_WEBSITE_JINAREADER = process.env.NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER !== undefined export const ENABLE_WEBSITE_JINAREADER = process.env.NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER !== undefined
? process.env.NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER === 'true' ? process.env.NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER === 'true'
: true : globalThis.document?.body?.getAttribute('data-public-enable-website-jinareader') === 'true' || true
export const ENABLE_WEBSITE_FIRECRAWL = process.env.NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL !== undefined export const ENABLE_WEBSITE_FIRECRAWL = process.env.NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL !== undefined
? process.env.NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL === 'true' ? process.env.NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL === 'true'
: true : globalThis.document?.body?.getAttribute('data-public-enable-website-firecrawl') === 'true' || true
export const ENABLE_WEBSITE_WATERCRAWL = process.env.NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL !== undefined export const ENABLE_WEBSITE_WATERCRAWL = process.env.NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL !== undefined
? process.env.NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL === 'true' ? process.env.NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL === 'true'
: true : globalThis.document?.body?.getAttribute('data-public-enable-website-watercrawl') === 'true' || true

@ -194,6 +194,17 @@ const translation = {
noParams: 'パラメータは必要ありません', noParams: 'パラメータは必要ありません',
placeholder: 'アプリを選択...', placeholder: 'アプリを選択...',
}, },
structOutput: {
moreFillTip: '最大10レベルのネストを表示します',
required: '必須',
LLMResponse: 'LLMのレスポンス',
configure: '設定',
notConfiguredTip: '構造化出力が未設定です',
structured: '構造化出力',
structuredTip: '構造化出力は、モデルが常に指定されたJSONスキーマに準拠した応答を生成することを保証する機能です。',
modelNotSupported: 'モデルが対応していません',
modelNotSupportedTip: '現在のモデルはこの機能に対応しておらず、自動的にプロンプトインジェクションに切り替わります。',
},
} }
export default translation export default translation

@ -56,6 +56,7 @@ const translation = {
viewDetails: '詳細を見る', viewDetails: '詳細を見る',
copied: 'コピーしました', copied: 'コピーしました',
in: '中', in: '中',
format: 'フォーマット',
}, },
errorMsg: { errorMsg: {
fieldRequired: '{{field}}は必要です', fieldRequired: '{{field}}は必要です',

@ -70,6 +70,10 @@ const translation = {
pasteHere: 'ここに貼り付け', pasteHere: 'ここに貼り付け',
pointerMode: 'ポインターモード', pointerMode: 'ポインターモード',
handMode: 'ハンドモード', handMode: 'ハンドモード',
exportImage: '画像を出力',
exportPNG: 'PNGで出力',
exportJPEG: 'JPEGで出力',
exportSVG: 'SVGで出力',
model: 'モデル', model: 'モデル',
workflowAsTool: 'ワークフローをツールどして公開する', workflowAsTool: 'ワークフローをツールどして公開する',
configureRequired: '設定が必要', configureRequired: '設定が必要',
@ -420,6 +424,34 @@ const translation = {
variable: '変数', variable: '変数',
}, },
sysQueryInUser: 'ユーザーメッセージにsys.queryを含めてください', sysQueryInUser: 'ユーザーメッセージにsys.queryを含めてください',
jsonSchema: {
title: '構造化データスキーマ',
instruction: '指示',
promptTooltip: 'テキスト説明から標準JSONスキーマを自動生成できます。',
promptPlaceholder: 'JSONスキーマを入力...',
generate: '生成',
import: 'JSONインポート',
generateJsonSchema: 'スキーマ生成',
generationTip: '自然言語で簡単にJSONスキーマを作成可能です。',
generating: 'JSONスキーマを生成中...',
generatedResult: '生成結果',
resultTip: 'こちらが生成された結果です。ご満足いただけない場合は、前の画面に戻ってプロンプトを修正できます。',
back: '前に戻る',
regenerate: '再生成する',
apply: '適用',
doc: '構造化出力の詳細を見る',
resetDefaults: '初期化',
required: '必須項目',
addField: 'フィールドを追加',
addChildField: 'サブフィールドを追加',
showAdvancedOptions: '詳細設定',
stringValidations: '文字列検証',
fieldNamePlaceholder: 'フィールド名',
descriptionPlaceholder: '説明を入力',
warningTips: {
saveSchema: '編集中のフィールドを確定してから保存してください。',
},
},
}, },
knowledgeRetrieval: { knowledgeRetrieval: {
queryVariable: '検索変数', queryVariable: '検索変数',

@ -1,4 +1,4 @@
const { basePath } = require('./utils/var-basePath') const { basePath, assetPrefix } = require('./utils/var-basePath')
const { codeInspectorPlugin } = require('code-inspector-plugin') const { codeInspectorPlugin } = require('code-inspector-plugin')
const withMDX = require('@next/mdx')({ const withMDX = require('@next/mdx')({
extension: /\.mdx?$/, extension: /\.mdx?$/,
@ -16,6 +16,7 @@ const withMDX = require('@next/mdx')({
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = { const nextConfig = {
basePath, basePath,
assetPrefix,
webpack: (config, { dev, isServer }) => { webpack: (config, { dev, isServer }) => {
config.plugins.push(codeInspectorPlugin({ bundler: 'webpack' })) config.plugins.push(codeInspectorPlugin({ bundler: 'webpack' }))
return config return config

@ -1,6 +1,6 @@
{ {
"name": "dify-web", "name": "dify-web",
"version": "1.2.0", "version": "1.3.0",
"private": true, "private": true,
"engines": { "engines": {
"node": ">=v22.11.0" "node": ">=v22.11.0"

@ -2,4 +2,5 @@
// same as the one exported from var.ts // same as the one exported from var.ts
module.exports = { module.exports = {
basePath: '', basePath: '',
assetPrefix: '',
} }

Loading…
Cancel
Save