From bcbdbed35265a4f999819858c232d7bea7598eb0 Mon Sep 17 00:00:00 2001
From: John Wang
Date: Mon, 15 May 2023 13:49:34 +0800
Subject: [PATCH 01/18] =?UTF-8?q?feat:=20Instructions=20for=20the=20initia?=
=?UTF-8?q?lization=20installation=20process=20after=20=E2=80=A6=20(#11)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 2 +-
README_CN.md | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 8b8369704e..8a6439b68d 100644
--- a/README.md
+++ b/README.md
@@ -41,7 +41,7 @@ cd docker
docker-compose up -d
```
-After running, you can access the Dify console in your browser at [http://localhost](http://localhost) and start the initialization operation.
+After running, you can access the Dify dashboard in your browser at [http://localhost/install](http://localhost/install) and start the initialization installation process.
### Configuration
diff --git a/README_CN.md b/README_CN.md
index b8d659cb06..4c819c4a49 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -43,7 +43,7 @@ cd docker
docker-compose up -d
```
-运行后,可以在浏览器上访问 [http://localhost](http://localhost) 进入 Dify 控制台,并开始初始化操作。
+运行后,可以在浏览器上访问 [http://localhost/install](http://localhost/install) 进入 Dify 控制台并开始初始化安装操作。
### 配置
From 4cfee55ec6bb44d3755b5ab4278314d42c7f21c9 Mon Sep 17 00:00:00 2001
From: Panmuse
Date: Mon, 15 May 2023 16:52:27 +0800
Subject: [PATCH 02/18] Update README.md (#17)
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 8a6439b68d..e80559bcf7 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
简体中文
-[Website](https://dify.ai) • [Docs](https://docs.dify.ai) • [Twitter](https://twitter.com/dify_ai)
+[Website](https://dify.ai) • [Docs](https://docs.dify.ai) • [Twitter](https://twitter.com/dify_ai) • [Discord](https://discord.gg/FngNHpbcY7)
**Dify** is an easy-to-use LLMOps platform designed to empower more people to create sustainable, AI-native applications. With visual orchestration for various application types, Dify offers out-of-the-box, ready-to-use applications that can also serve as Backend-as-a-Service APIs. Unify your development process with one API for plugins and datasets integration, and streamline your operations using a single interface for prompt engineering, visual analytics, and continuous improvement.
@@ -86,7 +86,7 @@ A: English and Chinese are currently supported, and you can contribute language
If you have any questions, suggestions, or partnership inquiries, feel free to contact us through the following channels:
- Submit an Issue or PR on our GitHub Repo
-- Join the discussion in our [Discord](https://discord.gg/AhzKf7dNgk) Community
+- Join the discussion in our [Discord](https://discord.gg/FngNHpbcY7) Community
- Send an email to hello@dify.ai
We're eager to assist you and together create more fun and useful AI applications!
From 805da40b1556fc54f48d15ccc1534d394e5fc8f2 Mon Sep 17 00:00:00 2001
From: Panmuse
Date: Mon, 15 May 2023 16:52:51 +0800
Subject: [PATCH 03/18] Update README_CN.md (#18)
---
README_CN.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README_CN.md b/README_CN.md
index 4c819c4a49..c72c03bd6a 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -5,7 +5,7 @@
-[官方网站](https://dify.ai) • [文档](https://docs.dify.ai/v/zh-hans) • [Twitter](https://twitter.com/dify_ai)
+[官方网站](https://dify.ai) • [文档](https://docs.dify.ai/v/zh-hans) • [Twitter](https://twitter.com/dify_ai) • [Discord](https://discord.gg/FngNHpbcY7)
**Dify** 是一个易用的 LLMOps 平台,旨在让更多人可以创建可持续运营的原生 AI 应用。Dify 提供多种类型应用的可视化编排,应用可开箱即用,也能以“后端即服务”的 API 提供服务。
@@ -87,7 +87,7 @@ A: 现已支持英文与中文,你可以为我们贡献语言包。
如果您有任何问题、建议或合作意向,欢迎通过以下方式联系我们:
- 在我们的 [GitHub Repo](https://github.com/langgenius/dify) 上提交 Issue 或 PR
-- 在我们的 [Discord 社区](https://discord.gg/AhzKf7dNgk) 上加入讨论
+- 在我们的 [Discord 社区](https://discord.gg/FngNHpbcY7) 上加入讨论
- 发送邮件至 hello@dify.ai
## 贡献代码
From c2e7fe107a2066646e5f237136272a0ac764529f Mon Sep 17 00:00:00 2001
From: Joel
Date: Mon, 15 May 2023 16:57:05 +0800
Subject: [PATCH 04/18] fix: nodejs endpoint (#20)
---
sdks/nodejs-client/index.js | 2 +-
sdks/nodejs-client/package.json | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/sdks/nodejs-client/index.js b/sdks/nodejs-client/index.js
index 902ecbd880..6faf5f5b06 100644
--- a/sdks/nodejs-client/index.js
+++ b/sdks/nodejs-client/index.js
@@ -17,7 +17,7 @@ const routes = {
},
createChatMessage: {
method: 'POST',
- url: () => `/chat-message`,
+ url: () => `/chat-messages`,
},
getConversationMessages: {
method: 'GET',
diff --git a/sdks/nodejs-client/package.json b/sdks/nodejs-client/package.json
index 350cfc9732..9c1a5ed5cf 100644
--- a/sdks/nodejs-client/package.json
+++ b/sdks/nodejs-client/package.json
@@ -1,6 +1,6 @@
{
"name": "dify-client",
- "version": "1.0.2",
+ "version": "1.0.3",
"description": "This is the Node.js SDK for the Dify.AI API, which allows you to easily integrate Dify.AI into your Node.js applications.",
"main": "index.js",
"type": "module",
From d8a716d85755a2044f59119cb488f2d50b0f606f Mon Sep 17 00:00:00 2001
From: John Wang
Date: Mon, 15 May 2023 16:57:50 +0800
Subject: [PATCH 05/18] fix: api and worker service volumes mount error (#21)
---
docker/docker-compose.yaml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml
index 40935d2926..4090ea9d4b 100644
--- a/docker/docker-compose.yaml
+++ b/docker/docker-compose.yaml
@@ -86,7 +86,7 @@ services:
- weaviate
volumes:
# Mount the storage directory to the container, for storing user files.
- - ./volumes/app/storage:/app/storage
+ - ./volumes/app/storage:/app/api/storage
# worker service
# The Celery worker for processing the queue.
@@ -137,7 +137,7 @@ services:
- weaviate
volumes:
# Mount the storage directory to the container, for storing user files.
- - ./volumes/app/storage:/app/storage
+ - ./volumes/app/storage:/app/api/storage
# Frontend web application.
web:
From 0eca93ebd1e4e775fcb1dd1a11c3c652067052fd Mon Sep 17 00:00:00 2001
From: John Wang
Date: Mon, 15 May 2023 17:33:35 +0800
Subject: [PATCH 06/18] feat: CORS and cookie policies have been relaxed. (#23)
---
docker/docker-compose.yaml | 21 +++++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml
index 4090ea9d4b..8f9de12ee9 100644
--- a/docker/docker-compose.yaml
+++ b/docker/docker-compose.yaml
@@ -43,16 +43,25 @@ services:
# The configurations of celery broker.
# Use redis as the broker, and redis db 1 for celery broker.
CELERY_BROKER_URL: redis://:difyai123456@redis:6379/1
- # Specifies the allowed origins for cross-origin requests to the Web API
- WEB_API_CORS_ALLOW_ORIGINS: http://localhost,*
- # Specifies the allowed origins for cross-origin requests to the console API
- CONSOLE_CORS_ALLOW_ORIGINS: http://localhost,*
+ # Specifies the allowed origins for cross-origin requests to the Web API, e.g. https://dify.app or * for all origins.
+ WEB_API_CORS_ALLOW_ORIGINS: '*'
+ # Specifies the allowed origins for cross-origin requests to the console API, e.g. https://cloud.dify.ai or * for all origins.
+ CONSOLE_CORS_ALLOW_ORIGINS: '*'
# CSRF Cookie settings
# Controls whether a cookie is sent with cross-site requests,
# providing some protection against cross-site request forgery attacks
+ #
+ # Default: `SameSite=Lax, Secure=false, HttpOnly=true`
+ # This default configuration supports same-origin requests using either HTTP or HTTPS,
+ # but does not support cross-origin requests. It is suitable for local debugging purposes.
+ #
+ # If you want to enable cross-origin support,
+ # you must use the HTTPS protocol and set the configuration to `SameSite=None, Secure=true, HttpOnly=true`.
+ #
+ # For **production** purposes, please set `SameSite=Lax, Secure=true, HttpOnly=true`.
COOKIE_HTTPONLY: 'true'
- COOKIE_SAMESITE: 'None'
- COOKIE_SECURE: 'true'
+ COOKIE_SAMESITE: 'Lax'
+ COOKIE_SECURE: 'false'
# The type of storage to use for storing user files. Supported values are `local` and `s3`, Default: `local`
STORAGE_TYPE: local
# The path to the local storage directory, the directory relative the root path of API service codes or absolute path. Default: `storage` or `/home/john/storage`.
From a8155cba7ecf7aab3b74e65c4b1da50890795309 Mon Sep 17 00:00:00 2001
From: John Wang
Date: Mon, 15 May 2023 19:26:50 +0800
Subject: [PATCH 07/18] feat: exclude full storage folder instead of only priv
folder in storage (#28)
---
.gitignore | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.gitignore b/.gitignore
index a3e3d66fed..50f682499e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -139,7 +139,7 @@ dmypy.json
api/.env
api/storage/*
-docker/volumes/app/storage/privkeys/*
+docker/volumes/app/storage/*
docker/volumes/db/data/*
docker/volumes/redis/data/*
docker/volumes/weaviate/*
From f5b2271c8c2d3ca88c24ea14932f0f03651952ed Mon Sep 17 00:00:00 2001
From: John Wang
Date: Mon, 15 May 2023 23:30:58 +0800
Subject: [PATCH 08/18] fix: import wrong user (#32)
---
api/core/conversation_message_task.py | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/api/core/conversation_message_task.py b/api/core/conversation_message_task.py
index 0df26637a3..81477533e7 100644
--- a/api/core/conversation_message_task.py
+++ b/api/core/conversation_message_task.py
@@ -2,8 +2,6 @@ import decimal
import json
from typing import Optional, Union
-from gunicorn.config import User
-
from core.callback_handler.entity.agent_loop import AgentLoop
from core.callback_handler.entity.dataset_query import DatasetQueryObj
from core.callback_handler.entity.llm_message import LLMMessage
@@ -269,7 +267,7 @@ class ConversationMessageTask:
class PubHandler:
- def __init__(self, user: Union[Account | User], task_id: str,
+ def __init__(self, user: Union[Account | EndUser], task_id: str,
message: Message, conversation: Conversation,
chain_pub: bool = False, agent_thought_pub: bool = False):
self._channel = PubHandler.generate_channel_name(user, task_id)
@@ -282,12 +280,12 @@ class PubHandler:
self._agent_thought_pub = agent_thought_pub
@classmethod
- def generate_channel_name(cls, user: Union[Account | User], task_id: str):
+ def generate_channel_name(cls, user: Union[Account | EndUser], task_id: str):
user_str = 'account-' + user.id if isinstance(user, Account) else 'end-user-' + user.id
return "generate_result:{}-{}".format(user_str, task_id)
@classmethod
- def generate_stopped_cache_key(cls, user: Union[Account | User], task_id: str):
+ def generate_stopped_cache_key(cls, user: Union[Account | EndUser], task_id: str):
user_str = 'account-' + user.id if isinstance(user, Account) else 'end-user-' + user.id
return "generate_result_stopped:{}-{}".format(user_str, task_id)
@@ -366,7 +364,7 @@ class PubHandler:
redis_client.publish(self._channel, json.dumps(content))
@classmethod
- def pub_error(cls, user: Union[Account | User], task_id: str, e):
+ def pub_error(cls, user: Union[Account | EndUser], task_id: str, e):
content = {
'error': type(e).__name__,
'description': e.description if getattr(e, 'description', None) is not None else str(e)
@@ -379,7 +377,7 @@ class PubHandler:
return redis_client.get(self._stopped_cache_key) is not None
@classmethod
- def stop(cls, user: Union[Account | User], task_id: str):
+ def stop(cls, user: Union[Account | EndUser], task_id: str):
stopped_cache_key = cls.generate_stopped_cache_key(user, task_id)
redis_client.setex(stopped_cache_key, 600, 1)
From 3117619ef3b99e94200a381082596ddfd4c49a12 Mon Sep 17 00:00:00 2001
From: crazywoola <100913391+crazywoola@users.noreply.github.com>
Date: Tue, 16 May 2023 09:22:47 +0800
Subject: [PATCH 09/18] Feature/add test to nodejs sdk (#31)
---
sdks/nodejs-client/babel.config.json | 5 +++
sdks/nodejs-client/index.js | 4 +-
sdks/nodejs-client/index.test.js | 66 ++++++++++++++++++++++++++++
sdks/nodejs-client/package.json | 14 ++++++
4 files changed, 87 insertions(+), 2 deletions(-)
create mode 100644 sdks/nodejs-client/babel.config.json
create mode 100644 sdks/nodejs-client/index.test.js
diff --git a/sdks/nodejs-client/babel.config.json b/sdks/nodejs-client/babel.config.json
new file mode 100644
index 0000000000..0639bf7643
--- /dev/null
+++ b/sdks/nodejs-client/babel.config.json
@@ -0,0 +1,5 @@
+{
+ "presets": [
+ "@babel/preset-env"
+ ]
+}
\ No newline at end of file
diff --git a/sdks/nodejs-client/index.js b/sdks/nodejs-client/index.js
index 6faf5f5b06..48b6ea753f 100644
--- a/sdks/nodejs-client/index.js
+++ b/sdks/nodejs-client/index.js
@@ -1,8 +1,8 @@
import axios from 'axios'
-const BASE_URL = 'https://api.dify.ai/v1'
+export const BASE_URL = 'https://api.dify.ai/v1'
-const routes = {
+export const routes = {
application: {
method: 'GET',
url: () => `/parameters`
diff --git a/sdks/nodejs-client/index.test.js b/sdks/nodejs-client/index.test.js
new file mode 100644
index 0000000000..e08b8e82af
--- /dev/null
+++ b/sdks/nodejs-client/index.test.js
@@ -0,0 +1,66 @@
+import { DifyClient, BASE_URL, routes } from ".";
+
+import axios from 'axios'
+
+jest.mock('axios')
+
+describe('Client', () => {
+ let difyClient
+ beforeEach(() => {
+ difyClient = new DifyClient('test')
+ })
+
+ test('should create a client', () => {
+ expect(difyClient).toBeDefined();
+ })
+ // test updateApiKey
+ test('should update the api key', () => {
+ difyClient.updateApiKey('test2');
+ expect(difyClient.apiKey).toBe('test2');
+ })
+});
+
+describe('Send Requests', () => {
+ let difyClient
+
+ beforeEach(() => {
+ difyClient = new DifyClient('test')
+ })
+
+ afterEach(() => {
+ jest.resetAllMocks()
+ })
+
+ it('should make a successful request to the application parameter', async () => {
+ const method = 'GET'
+ const endpoint = routes.application.url
+ const expectedResponse = { data: 'response' }
+ axios.mockResolvedValue(expectedResponse)
+
+ await difyClient.sendRequest(method, endpoint)
+
+ expect(axios).toHaveBeenCalledWith({
+ method,
+ url: `${BASE_URL}${endpoint}`,
+ data: null,
+ params: null,
+ headers: {
+ Authorization: `Bearer ${difyClient.apiKey}`,
+ 'Content-Type': 'application/json',
+ },
+ responseType: 'json',
+ })
+
+ })
+
+ it('should handle errors from the API', async () => {
+ const method = 'GET'
+ const endpoint = '/test-endpoint'
+ const errorMessage = 'Request failed with status code 404'
+ axios.mockRejectedValue(new Error(errorMessage))
+
+ await expect(difyClient.sendRequest(method, endpoint)).rejects.toThrow(
+ errorMessage
+ )
+ })
+})
\ No newline at end of file
diff --git a/sdks/nodejs-client/package.json b/sdks/nodejs-client/package.json
index 9c1a5ed5cf..c658b73d98 100644
--- a/sdks/nodejs-client/package.json
+++ b/sdks/nodejs-client/package.json
@@ -14,7 +14,21 @@
" <<427733928@qq.com>> (https://github.com/crazywoola)"
],
"license": "MIT",
+ "scripts": {
+ "test": "jest"
+ },
+ "jest": {
+ "transform": {
+ "^.+\\.[t|j]sx?$": "babel-jest"
+ }
+ },
"dependencies": {
"axios": "^1.3.5"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.21.8",
+ "@babel/preset-env": "^7.21.5",
+ "babel-jest": "^29.5.0",
+ "jest": "^29.5.0"
}
}
\ No newline at end of file
From 815f794eef4fdf4bcdc560ef189de4203aeea21a Mon Sep 17 00:00:00 2001
From: John Wang
Date: Tue, 16 May 2023 12:57:25 +0800
Subject: [PATCH 10/18] feat: optimize split rule when use custom split segment
identifier (#35)
---
.../index/spiltter/fixed_text_splitter.py | 68 +++++++++++++++++++
api/core/indexing_runner.py | 11 ++-
2 files changed, 73 insertions(+), 6 deletions(-)
create mode 100644 api/core/index/spiltter/fixed_text_splitter.py
diff --git a/api/core/index/spiltter/fixed_text_splitter.py b/api/core/index/spiltter/fixed_text_splitter.py
new file mode 100644
index 0000000000..aaaf8e5a10
--- /dev/null
+++ b/api/core/index/spiltter/fixed_text_splitter.py
@@ -0,0 +1,68 @@
+"""Functionality for splitting text."""
+from __future__ import annotations
+
+from typing import (
+ Any,
+ List,
+ Optional,
+)
+
+from langchain.text_splitter import RecursiveCharacterTextSplitter
+
+
+class FixedRecursiveCharacterTextSplitter(RecursiveCharacterTextSplitter):
+ def __init__(self, fixed_separator: str = "\n\n", separators: Optional[List[str]] = None, **kwargs: Any):
+ """Create a new TextSplitter."""
+ super().__init__(**kwargs)
+ self._fixed_separator = fixed_separator
+ self._separators = separators or ["\n\n", "\n", " ", ""]
+
+ def split_text(self, text: str) -> List[str]:
+ """Split incoming text and return chunks."""
+ if self._fixed_separator:
+ chunks = text.split(self._fixed_separator)
+ else:
+ chunks = list(text)
+
+ final_chunks = []
+ for chunk in chunks:
+ if self._length_function(chunk) > self._chunk_size:
+ final_chunks.extend(self.recursive_split_text(chunk))
+ else:
+ final_chunks.append(chunk)
+
+ return final_chunks
+
+ def recursive_split_text(self, text: str) -> List[str]:
+ """Split incoming text and return chunks."""
+ final_chunks = []
+ # Get appropriate separator to use
+ separator = self._separators[-1]
+ for _s in self._separators:
+ if _s == "":
+ separator = _s
+ break
+ if _s in text:
+ separator = _s
+ break
+ # Now that we have the separator, split the text
+ if separator:
+ splits = text.split(separator)
+ else:
+ splits = list(text)
+ # Now go merging things, recursively splitting longer texts.
+ _good_splits = []
+ for s in splits:
+ if self._length_function(s) < self._chunk_size:
+ _good_splits.append(s)
+ else:
+ if _good_splits:
+ merged_text = self._merge_splits(_good_splits, separator)
+ final_chunks.extend(merged_text)
+ _good_splits = []
+ other_info = self.recursive_split_text(s)
+ final_chunks.extend(other_info)
+ if _good_splits:
+ merged_text = self._merge_splits(_good_splits, separator)
+ final_chunks.extend(merged_text)
+ return final_chunks
diff --git a/api/core/indexing_runner.py b/api/core/indexing_runner.py
index fd9e430116..f06f3a0034 100644
--- a/api/core/indexing_runner.py
+++ b/api/core/indexing_runner.py
@@ -18,6 +18,7 @@ from core.docstore.dataset_docstore import DatesetDocumentStore
from core.index.keyword_table_index import KeywordTableIndex
from core.index.readers.html_parser import HTMLParser
from core.index.readers.pdf_parser import PDFParser
+from core.index.spiltter.fixed_text_splitter import FixedRecursiveCharacterTextSplitter
from core.index.vector_index import VectorIndex
from core.llm.token_calculator import TokenCalculator
from extensions.ext_database import db
@@ -267,16 +268,14 @@ class IndexingRunner:
raise ValueError("Custom segment length should be between 50 and 1000.")
separator = segmentation["separator"]
- if not separator:
- separators = ["\n\n", "。", ".", " ", ""]
- else:
+ if separator:
separator = separator.replace('\\n', '\n')
- separators = [separator, ""]
- character_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
+ character_splitter = FixedRecursiveCharacterTextSplitter.from_tiktoken_encoder(
chunk_size=segmentation["max_tokens"],
chunk_overlap=0,
- separators=separators
+ fixed_separator=separator,
+ separators=["\n\n", "。", ".", " ", ""]
)
else:
# Automatic segmentation
From 92fb4ab4c16e2619c11fa75ce440ffd571da7499 Mon Sep 17 00:00:00 2001
From: zxhlyh
Date: Tue, 16 May 2023 14:44:24 +0800
Subject: [PATCH 11/18] fix: help document link (#42)
---
web/app/components/header/account-dropdown/index.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/web/app/components/header/account-dropdown/index.tsx b/web/app/components/header/account-dropdown/index.tsx
index 2be1ad5559..b348ca1eca 100644
--- a/web/app/components/header/account-dropdown/index.tsx
+++ b/web/app/components/header/account-dropdown/index.tsx
@@ -89,7 +89,7 @@ export default function AppSelector({ userProfile, onLogout, langeniusVersionInf
{t('common.userProfile.helpCenter')}
From d680fca996f4c4275fa7434bd5290bb75192036c Mon Sep 17 00:00:00 2001
From: John Wang
Date: Tue, 16 May 2023 16:51:39 +0800
Subject: [PATCH 12/18] fix: provider_response_latency type error (#45)
---
api/controllers/console/app/conversation.py | 2 +-
api/controllers/console/app/message.py | 102 ++++++++++++--------
2 files changed, 65 insertions(+), 39 deletions(-)
diff --git a/api/controllers/console/app/conversation.py b/api/controllers/console/app/conversation.py
index 62752deaac..419068468d 100644
--- a/api/controllers/console/app/conversation.py
+++ b/api/controllers/console/app/conversation.py
@@ -45,7 +45,7 @@ message_detail_fields = {
'message_tokens': fields.Integer,
'answer': fields.String,
'answer_tokens': fields.Integer,
- 'provider_response_latency': fields.Integer,
+ 'provider_response_latency': fields.Float,
'from_source': fields.String,
'from_end_user_id': fields.String,
'from_account_id': fields.String,
diff --git a/api/controllers/console/app/message.py b/api/controllers/console/app/message.py
index 27698d965c..88594425de 100644
--- a/api/controllers/console/app/message.py
+++ b/api/controllers/console/app/message.py
@@ -26,46 +26,46 @@ from services.errors.conversation import ConversationNotExistsError
from services.errors.message import MessageNotExistsError
from services.message_service import MessageService
+account_fields = {
+ 'id': fields.String,
+ 'name': fields.String,
+ 'email': fields.String
+}
-class ChatMessageApi(Resource):
- account_fields = {
- 'id': fields.String,
- 'name': fields.String,
- 'email': fields.String
- }
+feedback_fields = {
+ 'rating': fields.String,
+ 'content': fields.String,
+ 'from_source': fields.String,
+ 'from_end_user_id': fields.String,
+ 'from_account': fields.Nested(account_fields, allow_null=True),
+}
- feedback_fields = {
- 'rating': fields.String,
- 'content': fields.String,
- 'from_source': fields.String,
- 'from_end_user_id': fields.String,
- 'from_account': fields.Nested(account_fields, allow_null=True),
- }
+annotation_fields = {
+ 'content': fields.String,
+ 'account': fields.Nested(account_fields, allow_null=True),
+ 'created_at': TimestampField
+}
- annotation_fields = {
- 'content': fields.String,
- 'account': fields.Nested(account_fields, allow_null=True),
- 'created_at': TimestampField
- }
+message_detail_fields = {
+ 'id': fields.String,
+ 'conversation_id': fields.String,
+ 'inputs': fields.Raw,
+ 'query': fields.String,
+ 'message': fields.Raw,
+ 'message_tokens': fields.Integer,
+ 'answer': fields.String,
+ 'answer_tokens': fields.Integer,
+ 'provider_response_latency': fields.Float,
+ 'from_source': fields.String,
+ 'from_end_user_id': fields.String,
+ 'from_account_id': fields.String,
+ 'feedbacks': fields.List(fields.Nested(feedback_fields)),
+ 'annotation': fields.Nested(annotation_fields, allow_null=True),
+ 'created_at': TimestampField
+}
- message_detail_fields = {
- 'id': fields.String,
- 'conversation_id': fields.String,
- 'inputs': fields.Raw,
- 'query': fields.String,
- 'message': fields.Raw,
- 'message_tokens': fields.Integer,
- 'answer': fields.String,
- 'answer_tokens': fields.Integer,
- 'provider_response_latency': fields.Integer,
- 'from_source': fields.String,
- 'from_end_user_id': fields.String,
- 'from_account_id': fields.String,
- 'feedbacks': fields.List(fields.Nested(feedback_fields)),
- 'annotation': fields.Nested(annotation_fields, allow_null=True),
- 'created_at': TimestampField
- }
+class ChatMessageListApi(Resource):
message_infinite_scroll_pagination_fields = {
'limit': fields.Integer,
'has_more': fields.Boolean,
@@ -253,7 +253,8 @@ class MessageMoreLikeThisApi(Resource):
message_id = str(message_id)
parser = reqparse.RequestParser()
- parser.add_argument('response_mode', type=str, required=True, choices=['blocking', 'streaming'], location='args')
+ parser.add_argument('response_mode', type=str, required=True, choices=['blocking', 'streaming'],
+ location='args')
args = parser.parse_args()
streaming = args['response_mode'] == 'streaming'
@@ -301,7 +302,8 @@ def compact_response(response: Union[dict | Generator]) -> Response:
except QuotaExceededError:
yield "data: " + json.dumps(api.handle_error(ProviderQuotaExceededError()).get_json()) + "\n\n"
except ModelCurrentlyNotSupportError:
- yield "data: " + json.dumps(api.handle_error(ProviderModelCurrentlyNotSupportError()).get_json()) + "\n\n"
+ yield "data: " + json.dumps(
+ api.handle_error(ProviderModelCurrentlyNotSupportError()).get_json()) + "\n\n"
except (LLMBadRequestError, LLMAPIConnectionError, LLMAPIUnavailableError,
LLMRateLimitError, LLMAuthorizationError) as e:
yield "data: " + json.dumps(api.handle_error(CompletionRequestError(str(e))).get_json()) + "\n\n"
@@ -353,9 +355,33 @@ class MessageSuggestedQuestionApi(Resource):
return {'data': questions}
+class MessageApi(Resource):
+ @setup_required
+ @login_required
+ @account_initialization_required
+ @marshal_with(message_detail_fields)
+ def get(self, app_id, message_id):
+ app_id = str(app_id)
+ message_id = str(message_id)
+
+ # get app info
+ app_model = _get_app(app_id, 'chat')
+
+ message = db.session.query(Message).filter(
+ Message.id == message_id,
+ Message.app_id == app_model.id
+ ).first()
+
+ if not message:
+ raise NotFound("Message Not Exists.")
+
+ return message
+
+
api.add_resource(MessageMoreLikeThisApi, '/apps//completion-messages//more-like-this')
api.add_resource(MessageSuggestedQuestionApi, '/apps//chat-messages//suggested-questions')
-api.add_resource(ChatMessageApi, '/apps//chat-messages', endpoint='chat_messages')
+api.add_resource(ChatMessageListApi, '/apps//chat-messages', endpoint='console_chat_messages')
api.add_resource(MessageFeedbackApi, '/apps//feedbacks')
api.add_resource(MessageAnnotationApi, '/apps//annotations')
api.add_resource(MessageAnnotationCountApi, '/apps//annotations/count')
+api.add_resource(MessageApi, '/apps//messages/', endpoint='console_message')
From 5fcd5c2499b701f4161cc0f3389c743ea6317885 Mon Sep 17 00:00:00 2001
From: Joel
Date: Tue, 16 May 2023 16:52:03 +0800
Subject: [PATCH 13/18] fix: spend time and token (#47)
---
web/app/components/app/chat/index.tsx | 3 ++-
web/app/components/app/configuration/debug/index.tsx | 4 ++--
web/app/components/app/log/list.tsx | 4 ++--
3 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/web/app/components/app/chat/index.tsx b/web/app/components/app/chat/index.tsx
index 7b512c57d4..577f0b1c1b 100644
--- a/web/app/components/app/chat/index.tsx
+++ b/web/app/components/app/chat/index.tsx
@@ -16,6 +16,7 @@ import type { Annotation, MessageRating } from '@/models/log'
import AppContext from '@/context/app-context'
import { Markdown } from '@/app/components/base/markdown'
import LoadingAnim from './loading-anim'
+import { formatNumber } from '@/utils/format'
const stopIcon = (