From 12836f9db9fb9da8eab85d414ddf0ec68f8f3434 Mon Sep 17 00:00:00 2001 From: Alex Chim <132866042+AlexChim1231@users.noreply.github.com> Date: Fri, 25 Apr 2025 11:52:25 +0800 Subject: [PATCH 1/3] Resolves #18536 Retreive conversation variables (#18581) --- .../service_api/app/conversation.py | 28 +++++ api/fields/conversation_variable_fields.py | 6 ++ api/services/conversation_service.py | 54 +++++++++- api/services/errors/conversation.py | 4 + .../template/template_advanced_chat.en.mdx | 100 ++++++++++++++++++ .../template/template_advanced_chat.ja.mdx | 100 ++++++++++++++++++ .../template/template_advanced_chat.zh.mdx | 100 ++++++++++++++++++ .../develop/template/template_chat.en.mdx | 100 ++++++++++++++++++ .../develop/template/template_chat.ja.mdx | 100 ++++++++++++++++++ .../develop/template/template_chat.zh.mdx | 100 ++++++++++++++++++ 10 files changed, 691 insertions(+), 1 deletion(-) diff --git a/api/controllers/service_api/app/conversation.py b/api/controllers/service_api/app/conversation.py index 334f2c5620..55600a3fd0 100644 --- a/api/controllers/service_api/app/conversation.py +++ b/api/controllers/service_api/app/conversation.py @@ -14,6 +14,9 @@ from fields.conversation_fields import ( conversation_infinite_scroll_pagination_fields, simple_conversation_fields, ) +from fields.conversation_variable_fields import ( + conversation_variable_infinite_scroll_pagination_fields, +) from libs.helper import uuid_value from models.model import App, AppMode, EndUser from services.conversation_service import ConversationService @@ -93,6 +96,31 @@ class ConversationRenameApi(Resource): raise NotFound("Conversation Not Exists.") +class ConversationVariablesApi(Resource): + @validate_app_token(fetch_user_arg=FetchUserArg(fetch_from=WhereisUserArg.QUERY)) + @marshal_with(conversation_variable_infinite_scroll_pagination_fields) + def get(self, app_model: App, end_user: EndUser, c_id): + # conversational variable only for chat app + app_mode = AppMode.value_of(app_model.mode) + if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT}: + raise NotChatAppError() + + conversation_id = str(c_id) + + parser = reqparse.RequestParser() + parser.add_argument("last_id", type=uuid_value, location="args") + parser.add_argument("limit", type=int_range(1, 100), required=False, default=20, location="args") + args = parser.parse_args() + + try: + return ConversationService.get_conversational_variable( + app_model, conversation_id, end_user, args["limit"], args["last_id"] + ) + except services.errors.conversation.ConversationNotExistsError: + raise NotFound("Conversation Not Exists.") + + api.add_resource(ConversationRenameApi, "/conversations//name", endpoint="conversation_name") api.add_resource(ConversationApi, "/conversations") api.add_resource(ConversationDetailApi, "/conversations/", endpoint="conversation_detail") +api.add_resource(ConversationVariablesApi, "/conversations//variables", endpoint="conversation_variables") diff --git a/api/fields/conversation_variable_fields.py b/api/fields/conversation_variable_fields.py index c6385efb5a..3aa3838def 100644 --- a/api/fields/conversation_variable_fields.py +++ b/api/fields/conversation_variable_fields.py @@ -19,3 +19,9 @@ paginated_conversation_variable_fields = { "has_more": fields.Boolean, "data": fields.List(fields.Nested(conversation_variable_fields), attribute="data"), } + +conversation_variable_infinite_scroll_pagination_fields = { + "limit": fields.Integer, + "has_more": fields.Boolean, + "data": fields.List(fields.Nested(conversation_variable_fields)), +} diff --git a/api/services/conversation_service.py b/api/services/conversation_service.py index 6485cbf37d..afdaa49465 100644 --- a/api/services/conversation_service.py +++ b/api/services/conversation_service.py @@ -9,9 +9,14 @@ from core.app.entities.app_invoke_entities import InvokeFrom from core.llm_generator.llm_generator import LLMGenerator from extensions.ext_database import db from libs.infinite_scroll_pagination import InfiniteScrollPagination +from models import ConversationVariable from models.account import Account from models.model import App, Conversation, EndUser, Message -from services.errors.conversation import ConversationNotExistsError, LastConversationNotExistsError +from services.errors.conversation import ( + ConversationNotExistsError, + ConversationVariableNotExistsError, + LastConversationNotExistsError, +) from services.errors.message import MessageNotExistsError @@ -166,3 +171,50 @@ class ConversationService: conversation.is_deleted = True conversation.updated_at = datetime.now(UTC).replace(tzinfo=None) db.session.commit() + + @classmethod + def get_conversational_variable( + cls, + app_model: App, + conversation_id: str, + user: Optional[Union[Account, EndUser]], + limit: int, + last_id: Optional[str], + ) -> InfiniteScrollPagination: + conversation = cls.get_conversation(app_model, conversation_id, user) + + stmt = ( + select(ConversationVariable) + .where(ConversationVariable.app_id == app_model.id) + .where(ConversationVariable.conversation_id == conversation.id) + .order_by(ConversationVariable.created_at) + ) + + with Session(db.engine) as session: + if last_id: + last_variable = session.scalar(stmt.where(ConversationVariable.id == last_id)) + if not last_variable: + raise ConversationVariableNotExistsError() + + # Filter for variables created after the last_id + stmt = stmt.where(ConversationVariable.created_at > last_variable.created_at) + + # Apply limit to query + query_stmt = stmt.limit(limit) # Get one extra to check if there are more + rows = session.scalars(query_stmt).all() + + has_more = False + if len(rows) > limit: + has_more = True + rows = rows[:limit] # Remove the extra item + + variables = [ + { + "created_at": row.created_at, + "updated_at": row.updated_at, + **row.to_variable().model_dump(), + } + for row in rows + ] + + return InfiniteScrollPagination(variables, limit, has_more) diff --git a/api/services/errors/conversation.py b/api/services/errors/conversation.py index 139dd9a70a..f8051e3417 100644 --- a/api/services/errors/conversation.py +++ b/api/services/errors/conversation.py @@ -11,3 +11,7 @@ class ConversationNotExistsError(BaseServiceError): class ConversationCompletedError(Exception): pass + + +class ConversationVariableNotExistsError(BaseServiceError): + pass diff --git a/web/app/components/develop/template/template_advanced_chat.en.mdx b/web/app/components/develop/template/template_advanced_chat.en.mdx index 9502f20124..f645133030 100644 --- a/web/app/components/develop/template/template_advanced_chat.en.mdx +++ b/web/app/components/develop/template/template_advanced_chat.en.mdx @@ -845,6 +845,106 @@ Chat applications support session persistence, allowing previous chat history to --- + + + + Retrieve variables from a specific conversation. This endpoint is useful for extracting structured data that was captured during the conversation. + + ### Path Parameters + + + + The ID of the conversation to retrieve variables from. + + + + ### Query Parameters + + + + The user identifier, defined by the developer, must ensure uniqueness within the application + + + (Optional) The ID of the last record on the current page, default is null. + + + (Optional) How many records to return in one request, default is the most recent 20 entries. Maximum 100, minimum 1. + + + + ### Response + + - `limit` (int) Number of items per page + - `has_more` (bool) Whether there is a next page + - `data` (array[object]) List of variables + - `id` (string) Variable ID + - `name` (string) Variable name + - `value_type` (string) Variable type (string, number, object, etc.) + - `value` (string) Variable value + - `description` (string) Variable description + - `created_at` (int) Creation timestamp + - `updated_at` (int) Last update timestamp + + ### Errors + - 404, `conversation_not_exists`, Conversation not found + + + + + + + ```bash {{ title: 'cURL' }} + curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + + ```bash {{ title: 'cURL' }} + curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123&variable_name=customer_name' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + ```json {{ title: 'Response' }} + { + "limit": 100, + "has_more": false, + "data": [ + { + "id": "variable-uuid-1", + "name": "customer_name", + "value_type": "string", + "value": "John Doe", + "description": "Customer name extracted from the conversation", + "created_at": 1650000000000, + "updated_at": 1650000000000 + }, + { + "id": "variable-uuid-2", + "name": "order_details", + "value_type": "json", + "value": "{\"product\":\"Widget\",\"quantity\":5,\"price\":19.99}", + "description": "Order details from the customer", + "created_at": 1650000000000, + "updated_at": 1650000000000 + } + ] + } + ``` + + + + +--- + + + + 特定の会話から変数を取得します。このエンドポイントは、会話中に取得された構造化データを抽出するのに役立ちます。 + + ### パスパラメータ + + + + 変数を取得する会話のID。 + + + + ### クエリパラメータ + + + + ユーザー識別子。開発者によって定義されたルールに従い、アプリケーション内で一意である必要があります。 + + + (Optional)現在のページの最後の記録のID、デフォルトはnullです。 + + + (Optional)1回のリクエストで返す記録の数、デフォルトは最新の20件です。最大100、最小1。 + + + + ### レスポンス + + - `limit` (int) ページごとのアイテム数 + - `has_more` (bool) さらにアイテムがあるかどうか + - `data` (array[object]) 変数のリスト + - `id` (string) 変数ID + - `name` (string) 変数名 + - `value_type` (string) 変数タイプ(文字列、数値、真偽値など) + - `value` (string) 変数値 + - `description` (string) 変数の説明 + - `created_at` (int) 作成タイムスタンプ + - `updated_at` (int) 最終更新タイムスタンプ + + ### エラー + - 404, `conversation_not_exists`, 会話が見つかりません + + + + + + + ```bash {{ title: 'cURL' }} + curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + + ```bash {{ title: 'cURL' }} + curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123&variable_name=customer_name' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + ```json {{ title: 'Response' }} + { + "limit": 100, + "has_more": false, + "data": [ + { + "id": "variable-uuid-1", + "name": "customer_name", + "value_type": "string", + "value": "John Doe", + "description": "会話から抽出された顧客名", + "created_at": 1650000000000, + "updated_at": 1650000000000 + }, + { + "id": "variable-uuid-2", + "name": "order_details", + "value_type": "json", + "value": "{\"product\":\"Widget\",\"quantity\":5,\"price\":19.99}", + "description": "顧客の注文詳細", + "created_at": 1650000000000, + "updated_at": 1650000000000 + } + ] + } + ``` + + + + +--- + + + + 从特定对话中检索变量。此端点对于提取对话过程中捕获的结构化数据非常有用。 + + ### 路径参数 + + + + 要从中检索变量的对话ID。 + + + + ### 查询参数 + + + + 用户标识符,由开发人员定义的规则,在应用程序内必须唯一。 + + + (选填)当前页最后面一条记录的 ID,默认 null + + + (选填)一次请求返回多少条记录,默认 20 条,最大 100 条,最小 1 条。 + + + + ### 响应 + + - `limit` (int) 每页项目数 + - `has_more` (bool) 是否有更多项目 + - `data` (array[object]) 变量列表 + - `id` (string) 变量ID + - `name` (string) 变量名称 + - `value_type` (string) 变量类型(字符串、数字、布尔等) + - `value` (string) 变量值 + - `description` (string) 变量描述 + - `created_at` (int) 创建时间戳 + - `updated_at` (int) 最后更新时间戳 + + ### 错误 + - 404, `conversation_not_exists`, 对话不存在 + + + + + + + ```bash {{ title: 'cURL' }} + curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + + ```bash {{ title: 'cURL' }} + curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123&variable_name=customer_name' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + ```json {{ title: 'Response' }} + { + "limit": 100, + "has_more": false, + "data": [ + { + "id": "variable-uuid-1", + "name": "customer_name", + "value_type": "string", + "value": "John Doe", + "description": "客户名称(从对话中提取)", + "created_at": 1650000000000, + "updated_at": 1650000000000 + }, + { + "id": "variable-uuid-2", + "name": "order_details", + "value_type": "json", + "value": "{\"product\":\"Widget\",\"quantity\":5,\"price\":19.99}", + "description": "客户的订单详情", + "created_at": 1650000000000, + "updated_at": 1650000000000 + } + ] + } + ``` + + + + +--- + + + + Retrieve variables from a specific conversation. This endpoint is useful for extracting structured data that was captured during the conversation. + + ### Path Parameters + + + + The ID of the conversation to retrieve variables from. + + + + ### Query Parameters + + + + The user identifier, defined by the developer, must ensure uniqueness within the application + + + (Optional) The ID of the last record on the current page, default is null. + + + (Optional) How many records to return in one request, default is the most recent 20 entries. Maximum 100, minimum 1. + + + + ### Response + + - `limit` (int) Number of items per page + - `has_more` (bool) Whether there is a next page + - `data` (array[object]) List of variables + - `id` (string) Variable ID + - `name` (string) Variable name + - `value_type` (string) Variable type (string, number, object, etc.) + - `value` (string) Variable value + - `description` (string) Variable description + - `created_at` (int) Creation timestamp + - `updated_at` (int) Last update timestamp + + ### Errors + - 404, `conversation_not_exists`, Conversation not found + + + + + + + ```bash {{ title: 'cURL' }} + curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + + ```bash {{ title: 'cURL' }} + curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123&variable_name=customer_name' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + ```json {{ title: 'Response' }} + { + "limit": 100, + "has_more": false, + "data": [ + { + "id": "variable-uuid-1", + "name": "customer_name", + "value_type": "string", + "value": "John Doe", + "description": "Customer name extracted from the conversation", + "created_at": 1650000000000, + "updated_at": 1650000000000 + }, + { + "id": "variable-uuid-2", + "name": "order_details", + "value_type": "json", + "value": "{\"product\":\"Widget\",\"quantity\":5,\"price\":19.99}", + "description": "Order details from the customer", + "created_at": 1650000000000, + "updated_at": 1650000000000 + } + ] + } + ``` + + + + +--- + + + + 特定の会話から変数を取得します。このエンドポイントは、会話中に取得された構造化データを抽出するのに役立ちます。 + + ### パスパラメータ + + + + 変数を取得する会話のID。 + + + + ### クエリパラメータ + + + + ユーザー識別子。開発者によって定義されたルールに従い、アプリケーション内で一意である必要があります。 + + + (Optional)現在のページの最後のレコードのID、デフォルトはnullです。 + + + (Optional)1回のリクエストで返すレコードの数、デフォルトは最新の20件です。最大100、最小1。 + + + + ### レスポンス + + - `limit` (int) ページごとのアイテム数 + - `has_more` (bool) さらにアイテムがあるかどうか + - `data` (array[object]) 変数のリスト + - `id` (string) 変数ID + - `name` (string) 変数名 + - `value_type` (string) 変数タイプ(文字列、数値、真偽値など) + - `value` (string) 変数値 + - `description` (string) 変数の説明 + - `created_at` (int) 作成タイムスタンプ + - `updated_at` (int) 最終更新タイムスタンプ + + ### エラー + - 404, `conversation_not_exists`, 会話が見つかりません + + + + + + + ```bash {{ title: 'cURL' }} + curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + + ```bash {{ title: 'cURL' }} + curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123&variable_name=customer_name' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + ```json {{ title: 'Response' }} + { + "limit": 100, + "has_more": false, + "data": [ + { + "id": "variable-uuid-1", + "name": "customer_name", + "value_type": "string", + "value": "John Doe", + "description": "会話から抽出された顧客名", + "created_at": 1650000000000, + "updated_at": 1650000000000 + }, + { + "id": "variable-uuid-2", + "name": "order_details", + "value_type": "json", + "value": "{\"product\":\"Widget\",\"quantity\":5,\"price\":19.99}", + "description": "顧客の注文詳細", + "created_at": 1650000000000, + "updated_at": 1650000000000 + } + ] + } + ``` + + + + +--- + + + + 从特定对话中检索变量。此端点对于提取对话过程中捕获的结构化数据非常有用。 + + ### 路径参数 + + + + 要从中检索变量的对话ID。 + + + + ### 查询参数 + + + + 用户标识符,由开发人员定义的规则,在应用程序内必须唯一。 + + + (选填)当前页最后面一条记录的 ID,默认 null + + + (选填)一次请求返回多少条记录,默认 20 条,最大 100 条,最小 1 条。 + + + + ### 响应 + + - `limit` (int) 每页项目数 + - `has_more` (bool) 是否有更多项目 + - `data` (array[object]) 变量列表 + - `id` (string) 变量ID + - `name` (string) 变量名称 + - `value_type` (string) 变量类型(字符串、数字、布尔等) + - `value` (string) 变量值 + - `description` (string) 变量描述 + - `created_at` (int) 创建时间戳 + - `updated_at` (int) 最后更新时间戳 + + ### 错误 + - 404, `conversation_not_exists`, 对话不存在 + + + + + + + ```bash {{ title: 'cURL' }} + curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + + ```bash {{ title: 'cURL' }} + curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123&variable_name=customer_name' \ + --header 'Authorization: Bearer {api_key}' + ``` + + + + ```json {{ title: 'Response' }} + { + "limit": 100, + "has_more": false, + "data": [ + { + "id": "variable-uuid-1", + "name": "customer_name", + "value_type": "string", + "value": "John Doe", + "description": "客户名称(从对话中提取)", + "created_at": 1650000000000, + "updated_at": 1650000000000 + }, + { + "id": "variable-uuid-2", + "name": "order_details", + "value_type": "json", + "value": "{\"product\":\"Widget\",\"quantity\":5,\"price\":19.99}", + "description": "客户的订单详情", + "created_at": 1650000000000, + "updated_at": 1650000000000 + } + ] + } + ``` + + + + +--- + Date: Fri, 25 Apr 2025 12:12:30 +0800 Subject: [PATCH 2/3] fix: enable Milvus database configuration via .env(#18707) (#18714) Co-authored-by: jiawei.wang Co-authored-by: crazywoola <427733928@qq.com> --- docker/.env.example | 1 + docker/docker-compose.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/docker/.env.example b/docker/.env.example index 5c5ee4b548..83d975cec5 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -406,6 +406,7 @@ QDRANT_GRPC_PORT=6334 # Milvus configuration. Only available when VECTOR_STORE is `milvus`. # The milvus uri. MILVUS_URI=http://host.docker.internal:19530 +MILVUS_DATABASE= MILVUS_TOKEN= MILVUS_USER= MILVUS_PASSWORD= diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index c8b7a006e9..8edcd497c6 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -138,6 +138,7 @@ x-shared-env: &shared-api-worker-env QDRANT_GRPC_ENABLED: ${QDRANT_GRPC_ENABLED:-false} QDRANT_GRPC_PORT: ${QDRANT_GRPC_PORT:-6334} MILVUS_URI: ${MILVUS_URI:-http://host.docker.internal:19530} + MILVUS_DATABASE: ${MILVUS_DATABASE:-} MILVUS_TOKEN: ${MILVUS_TOKEN:-} MILVUS_USER: ${MILVUS_USER:-} MILVUS_PASSWORD: ${MILVUS_PASSWORD:-} From fc4e11d12760ccdeccfa59c96672eba5aeb1d0f6 Mon Sep 17 00:00:00 2001 From: crStiv Date: Fri, 25 Apr 2025 08:42:10 +0300 Subject: [PATCH 3/3] fix: wording in README.md (#18751) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 65e8001dd2..efb37d6083 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ README in বাংলা

-Dify is an open-source LLM app development platform. Its intuitive interface combines agentic AI workflow, RAG pipeline, agent capabilities, model management, observability features and more, letting you quickly go from prototype to production. +Dify is an open-source LLM app development platform. Its intuitive interface combines agentic AI workflow, RAG pipeline, agent capabilities, model management, observability features, and more, allowing you to quickly move from prototype to production. ## Quick start @@ -188,7 +188,7 @@ All of Dify's offerings come with corresponding APIs, so you could effortlessly - **Dify for enterprise / organizations
** We provide additional enterprise-centric features. [Log your questions for us through this chatbot](https://udify.app/chat/22L1zSxg6yW1cWQg) or [send us an email](mailto:business@dify.ai?subject=[GitHub]Business%20License%20Inquiry) to discuss enterprise needs.
- > For startups and small businesses using AWS, check out [Dify Premium on AWS Marketplace](https://aws.amazon.com/marketplace/pp/prodview-t22mebxzwjhu6) and deploy it to your own AWS VPC with one-click. It's an affordable AMI offering with the option to create apps with custom logo and branding. + > For startups and small businesses using AWS, check out [Dify Premium on AWS Marketplace](https://aws.amazon.com/marketplace/pp/prodview-t22mebxzwjhu6) and deploy it to your own AWS VPC with one click. It's an affordable AMI offering with the option to create apps with custom logo and branding. ## Staying ahead @@ -233,7 +233,7 @@ Deploy Dify to AWS with [CDK](https://aws.amazon.com/cdk/) For those who'd like to contribute code, see our [Contribution Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md). At the same time, please consider supporting Dify by sharing it on social media and at events and conferences. -> We are looking for contributors to help with translating Dify to languages other than Mandarin or English. If you are interested in helping, please see the [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n/README.md) for more information, and leave us a comment in the `global-users` channel of our [Discord Community Server](https://discord.gg/8Tpq4AcN9c). +> We are looking for contributors to help translate Dify into languages other than Mandarin or English. If you are interested in helping, please see the [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n/README.md) for more information, and leave us a comment in the `global-users` channel of our [Discord Community Server](https://discord.gg/8Tpq4AcN9c). ## Community & contact