Resolves #18536 Retreive conversation variables (#18581)

This commit is contained in:
Alex Chim 2025-04-25 11:52:25 +08:00 committed by GitHub
parent 2627e221f2
commit 12836f9db9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 691 additions and 1 deletions

View File

@ -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/<uuid:c_id>/name", endpoint="conversation_name")
api.add_resource(ConversationApi, "/conversations")
api.add_resource(ConversationDetailApi, "/conversations/<uuid:c_id>", endpoint="conversation_detail")
api.add_resource(ConversationVariablesApi, "/conversations/<uuid:c_id>/variables", endpoint="conversation_variables")

View File

@ -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)),
}

View File

@ -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)

View File

@ -11,3 +11,7 @@ class ConversationNotExistsError(BaseServiceError):
class ConversationCompletedError(Exception):
pass
class ConversationVariableNotExistsError(BaseServiceError):
pass

View File

@ -845,6 +845,106 @@ Chat applications support session persistence, allowing previous chat history to
---
<Heading
url='/conversations/:conversation_id/variables'
method='GET'
title='Get Conversation Variables'
name='#conversation-variables'
/>
<Row>
<Col>
Retrieve variables from a specific conversation. This endpoint is useful for extracting structured data that was captured during the conversation.
### Path Parameters
<Properties>
<Property name='conversation_id' type='string' key='conversation_id'>
The ID of the conversation to retrieve variables from.
</Property>
</Properties>
### Query Parameters
<Properties>
<Property name='user' type='string' key='user'>
The user identifier, defined by the developer, must ensure uniqueness within the application
</Property>
<Property name='last_id' type='string' key='last_id'>
(Optional) The ID of the last record on the current page, default is null.
</Property>
<Property name='limit' type='int' key='limit'>
(Optional) How many records to return in one request, default is the most recent 20 entries. Maximum 100, minimum 1.
</Property>
</Properties>
### 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
</Col>
<Col sticky>
<CodeGroup title="Request" tag="GET" label="/conversations/:conversation_id/variables" targetCode={`curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123' \\\n--header 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Request with variable name filter">
```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}'
```
</CodeGroup>
<CodeGroup title="Response">
```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
}
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/audio-to-text'
method='POST'

View File

@ -844,6 +844,106 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
---
<Heading
url='/conversations/:conversation_id/variables'
method='GET'
title='会話変数の取得'
name='#conversation-variables'
/>
<Row>
<Col>
特定の会話から変数を取得します。このエンドポイントは、会話中に取得された構造化データを抽出するのに役立ちます。
### パスパラメータ
<Properties>
<Property name='conversation_id' type='string' key='conversation_id'>
変数を取得する会話のID。
</Property>
</Properties>
### クエリパラメータ
<Properties>
<Property name='user' type='string' key='user'>
ユーザー識別子。開発者によって定義されたルールに従い、アプリケーション内で一意である必要があります。
</Property>
<Property name='last_id' type='string' key='last_id'>
(Optional)現在のページの最後の記録のID、デフォルトはnullです。
</Property>
<Property name='limit' type='int' key='limit'>
(Optional)1回のリクエストで返す記録の数、デフォルトは最新の20件です。最大100、最小1。
</Property>
</Properties>
### レスポンス
- `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`, 会話が見つかりません
</Col>
<Col sticky>
<CodeGroup title="Request" tag="GET" label="/conversations/:conversation_id/variables" targetCode={`curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123' \\\n--header 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Request with variable name filter">
```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}'
```
</CodeGroup>
<CodeGroup title="Response">
```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
}
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/audio-to-text'
method='POST'

View File

@ -881,6 +881,106 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
---
<Heading
url='/conversations/:conversation_id/variables'
method='GET'
title='获取对话变量'
name='#conversation-variables'
/>
<Row>
<Col>
从特定对话中检索变量。此端点对于提取对话过程中捕获的结构化数据非常有用。
### 路径参数
<Properties>
<Property name='conversation_id' type='string' key='conversation_id'>
要从中检索变量的对话ID。
</Property>
</Properties>
### 查询参数
<Properties>
<Property name='user' type='string' key='user'>
用户标识符,由开发人员定义的规则,在应用程序内必须唯一。
</Property>
<Property name='last_id' type='string' key='last_id'>
(选填)当前页最后面一条记录的 ID默认 null
</Property>
<Property name='limit' type='int' key='limit'>
(选填)一次请求返回多少条记录,默认 20 条,最大 100 条,最小 1 条。
</Property>
</Properties>
### 响应
- `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`, 对话不存在
</Col>
<Col sticky>
<CodeGroup title="Request" tag="GET" label="/conversations/:conversation_id/variables" targetCode={`curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123' \\\n--header 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Request with variable name filter">
```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}'
```
</CodeGroup>
<CodeGroup title="Response">
```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
}
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/audio-to-text'
method='POST'

View File

@ -878,6 +878,106 @@ Chat applications support session persistence, allowing previous chat history to
---
<Heading
url='/conversations/:conversation_id/variables'
method='GET'
title='Get Conversation Variables'
name='#conversation-variables'
/>
<Row>
<Col>
Retrieve variables from a specific conversation. This endpoint is useful for extracting structured data that was captured during the conversation.
### Path Parameters
<Properties>
<Property name='conversation_id' type='string' key='conversation_id'>
The ID of the conversation to retrieve variables from.
</Property>
</Properties>
### Query Parameters
<Properties>
<Property name='user' type='string' key='user'>
The user identifier, defined by the developer, must ensure uniqueness within the application
</Property>
<Property name='last_id' type='string' key='last_id'>
(Optional) The ID of the last record on the current page, default is null.
</Property>
<Property name='limit' type='int' key='limit'>
(Optional) How many records to return in one request, default is the most recent 20 entries. Maximum 100, minimum 1.
</Property>
</Properties>
### 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
</Col>
<Col sticky>
<CodeGroup title="Request" tag="GET" label="/conversations/:conversation_id/variables" targetCode={`curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123' \\\n--header 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Request with variable name filter">
```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}'
```
</CodeGroup>
<CodeGroup title="Response">
```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
}
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/audio-to-text'
method='POST'

View File

@ -876,6 +876,106 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
---
<Heading
url='/conversations/:conversation_id/variables'
method='GET'
title='会話変数の取得'
name='#conversation-variables'
/>
<Row>
<Col>
特定の会話から変数を取得します。このエンドポイントは、会話中に取得された構造化データを抽出するのに役立ちます。
### パスパラメータ
<Properties>
<Property name='conversation_id' type='string' key='conversation_id'>
変数を取得する会話のID。
</Property>
</Properties>
### クエリパラメータ
<Properties>
<Property name='user' type='string' key='user'>
ユーザー識別子。開発者によって定義されたルールに従い、アプリケーション内で一意である必要があります。
</Property>
<Property name='last_id' type='string' key='last_id'>
(Optional)現在のページの最後のレコードのID、デフォルトはnullです。
</Property>
<Property name='limit' type='int' key='limit'>
(Optional)1回のリクエストで返すレコードの数、デフォルトは最新の20件です。最大100、最小1。
</Property>
</Properties>
### レスポンス
- `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`, 会話が見つかりません
</Col>
<Col sticky>
<CodeGroup title="Request" tag="GET" label="/conversations/:conversation_id/variables" targetCode={`curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123' \\\n--header 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Request with variable name filter">
```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}'
```
</CodeGroup>
<CodeGroup title="Response">
```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
}
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/audio-to-text'
method='POST'

View File

@ -893,6 +893,106 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
---
<Heading
url='/conversations/:conversation_id/variables'
method='GET'
title='获取对话变量'
name='#conversation-variables'
/>
<Row>
<Col>
从特定对话中检索变量。此端点对于提取对话过程中捕获的结构化数据非常有用。
### 路径参数
<Properties>
<Property name='conversation_id' type='string' key='conversation_id'>
要从中检索变量的对话ID。
</Property>
</Properties>
### 查询参数
<Properties>
<Property name='user' type='string' key='user'>
用户标识符,由开发人员定义的规则,在应用程序内必须唯一。
</Property>
<Property name='last_id' type='string' key='last_id'>
(选填)当前页最后面一条记录的 ID默认 null
</Property>
<Property name='limit' type='int' key='limit'>
(选填)一次请求返回多少条记录,默认 20 条,最大 100 条,最小 1 条。
</Property>
</Properties>
### 响应
- `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`, 对话不存在
</Col>
<Col sticky>
<CodeGroup title="Request" tag="GET" label="/conversations/:conversation_id/variables" targetCode={`curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123' \\\n--header 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables?user=abc-123' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Request with variable name filter">
```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}'
```
</CodeGroup>
<CodeGroup title="Response">
```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
}
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/audio-to-text'
method='POST'