+18


![dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>](/assets/img/avatar_default.png)

![autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>](/assets/img/avatar_default.png)




FFXN
GitHub
yyh
盐粒 Yanli
autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Tianle
dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Yunlu Wen
zyssyz123
Claude Opus 4.7
chariri
Asuka Minato
Copilot Autofix powered by AI
Nian
非法操作
Carmen Fernández Ruiz
wangxiaolei
QuantumGhost
L1nSn0w
Evan
Escape0707
Jingyi
Amr Sherif
ZHOU ZHICHEN
unknown
JzoNg
Xiyuan Chen
-LAN-
107bba0116
Signed-off-by: dependabot[bot] <[email protected]> Signed-off-by: EvanYao826 <[email protected]> Co-authored-by: yyh <[email protected]> Co-authored-by: 盐粒 Yanli <[email protected]> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Tianle <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Yunlu Wen <[email protected]> Co-authored-by: zyssyz123 <[email protected]> Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]> Co-authored-by: chariri <[email protected]> Co-authored-by: Asuka Minato <[email protected]> Co-authored-by: Copilot Autofix powered by AI <[email protected]> Co-authored-by: Nian <[email protected]> Co-authored-by: 非法操作 <[email protected]> Co-authored-by: Carmen Fernández Ruiz <[email protected]> Co-authored-by: wangxiaolei <[email protected]> Co-authored-by: QuantumGhost <[email protected]> Co-authored-by: L1nSn0w <[email protected]> Co-authored-by: Evan <[email protected]> Co-authored-by: Escape0707 <[email protected]> Co-authored-by: Jingyi <[email protected]> Co-authored-by: Amr Sherif <[email protected]> Co-authored-by: ZHOU ZHICHEN <[email protected]> Co-authored-by: unknown <[email protected]> Co-authored-by: JzoNg <[email protected]> Co-authored-by: Xiyuan Chen <[email protected]> Co-authored-by: -LAN- <[email protected]>
196 lines
7.9 KiB
Python
196 lines
7.9 KiB
Python
from typing import Any
|
|
from uuid import UUID
|
|
|
|
from flask import request
|
|
from pydantic import BaseModel, Field, TypeAdapter
|
|
from sqlalchemy.orm import sessionmaker
|
|
from werkzeug.exceptions import NotFound
|
|
|
|
from controllers.common.controller_schemas import ConversationRenamePayload
|
|
from controllers.common.schema import register_response_schema_models, register_schema_models
|
|
from controllers.console.app.error import AppUnavailableError
|
|
from controllers.console.explore.error import NotChatAppError
|
|
from controllers.console.explore.wraps import InstalledAppResource
|
|
from core.app.entities.app_invoke_entities import InvokeFrom
|
|
from extensions.ext_database import db
|
|
from fields.conversation_fields import (
|
|
ConversationInfiniteScrollPagination,
|
|
ResultResponse,
|
|
SimpleConversation,
|
|
)
|
|
from libs.helper import UUIDStrOrEmpty
|
|
from libs.login import current_user
|
|
from models import Account
|
|
from models.model import AppMode, InstalledApp
|
|
from services.conversation_service import ConversationService
|
|
from services.errors.conversation import ConversationNotExistsError, LastConversationNotExistsError
|
|
from services.web_conversation_service import WebConversationService
|
|
|
|
from .. import console_ns
|
|
|
|
|
|
class ConversationListQuery(BaseModel):
|
|
last_id: UUIDStrOrEmpty | None = None
|
|
limit: int = Field(default=20, ge=1, le=100)
|
|
pinned: bool | None = None
|
|
|
|
|
|
register_schema_models(console_ns, ConversationListQuery, ConversationRenamePayload)
|
|
register_response_schema_models(console_ns, ResultResponse)
|
|
|
|
|
|
@console_ns.route(
|
|
"/installed-apps/<uuid:installed_app_id>/conversations",
|
|
endpoint="installed_app_conversations",
|
|
)
|
|
class ConversationListApi(InstalledAppResource):
|
|
@console_ns.expect(console_ns.models[ConversationListQuery.__name__])
|
|
def get(self, installed_app: InstalledApp):
|
|
app_model = installed_app.app
|
|
if app_model is None:
|
|
raise AppUnavailableError()
|
|
app_mode = AppMode.value_of(app_model.mode)
|
|
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT}:
|
|
raise NotChatAppError()
|
|
|
|
raw_args: dict[str, Any] = {
|
|
"last_id": request.args.get("last_id"),
|
|
"limit": request.args.get("limit", default=20, type=int),
|
|
"pinned": request.args.get("pinned"),
|
|
}
|
|
if raw_args["last_id"] is None:
|
|
raw_args["last_id"] = None
|
|
pinned_value = raw_args["pinned"]
|
|
if isinstance(pinned_value, str):
|
|
raw_args["pinned"] = pinned_value == "true"
|
|
args = ConversationListQuery.model_validate(raw_args)
|
|
|
|
try:
|
|
if not isinstance(current_user, Account):
|
|
raise ValueError("current_user must be an Account instance")
|
|
with sessionmaker(db.engine).begin() as session:
|
|
pagination = WebConversationService.pagination_by_last_id(
|
|
session=session,
|
|
app_model=app_model,
|
|
user=current_user,
|
|
last_id=str(args.last_id) if args.last_id else None,
|
|
limit=args.limit,
|
|
invoke_from=InvokeFrom.EXPLORE,
|
|
pinned=args.pinned,
|
|
)
|
|
adapter = TypeAdapter(SimpleConversation)
|
|
conversations = [adapter.validate_python(item, from_attributes=True) for item in pagination.data]
|
|
return ConversationInfiniteScrollPagination(
|
|
limit=pagination.limit,
|
|
has_more=pagination.has_more,
|
|
data=conversations,
|
|
).model_dump(mode="json")
|
|
except LastConversationNotExistsError:
|
|
raise NotFound("Last Conversation Not Exists.")
|
|
|
|
|
|
@console_ns.route(
|
|
"/installed-apps/<uuid:installed_app_id>/conversations/<uuid:c_id>",
|
|
endpoint="installed_app_conversation",
|
|
)
|
|
class ConversationApi(InstalledAppResource):
|
|
@console_ns.response(204, "Conversation deleted successfully")
|
|
def delete(self, installed_app: InstalledApp, c_id: UUID):
|
|
app_model = installed_app.app
|
|
if app_model is None:
|
|
raise AppUnavailableError()
|
|
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)
|
|
try:
|
|
if not isinstance(current_user, Account):
|
|
raise ValueError("current_user must be an Account instance")
|
|
ConversationService.delete(app_model, conversation_id, current_user)
|
|
except ConversationNotExistsError:
|
|
raise NotFound("Conversation Not Exists.")
|
|
|
|
return "", 204
|
|
|
|
|
|
@console_ns.route(
|
|
"/installed-apps/<uuid:installed_app_id>/conversations/<uuid:c_id>/name",
|
|
endpoint="installed_app_conversation_rename",
|
|
)
|
|
class ConversationRenameApi(InstalledAppResource):
|
|
@console_ns.expect(console_ns.models[ConversationRenamePayload.__name__])
|
|
def post(self, installed_app: InstalledApp, c_id: UUID):
|
|
app_model = installed_app.app
|
|
if app_model is None:
|
|
raise AppUnavailableError()
|
|
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)
|
|
|
|
payload = ConversationRenamePayload.model_validate(console_ns.payload or {})
|
|
|
|
try:
|
|
if not isinstance(current_user, Account):
|
|
raise ValueError("current_user must be an Account instance")
|
|
conversation = ConversationService.rename(
|
|
app_model, conversation_id, current_user, payload.name, payload.auto_generate
|
|
)
|
|
return (
|
|
TypeAdapter(SimpleConversation)
|
|
.validate_python(conversation, from_attributes=True)
|
|
.model_dump(mode="json")
|
|
)
|
|
except ConversationNotExistsError:
|
|
raise NotFound("Conversation Not Exists.")
|
|
|
|
|
|
@console_ns.route(
|
|
"/installed-apps/<uuid:installed_app_id>/conversations/<uuid:c_id>/pin",
|
|
endpoint="installed_app_conversation_pin",
|
|
)
|
|
class ConversationPinApi(InstalledAppResource):
|
|
@console_ns.response(200, "Success", console_ns.models[ResultResponse.__name__])
|
|
def patch(self, installed_app: InstalledApp, c_id: UUID):
|
|
app_model = installed_app.app
|
|
if app_model is None:
|
|
raise AppUnavailableError()
|
|
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)
|
|
|
|
try:
|
|
if not isinstance(current_user, Account):
|
|
raise ValueError("current_user must be an Account instance")
|
|
WebConversationService.pin(app_model, conversation_id, current_user)
|
|
except ConversationNotExistsError:
|
|
raise NotFound("Conversation Not Exists.")
|
|
|
|
return ResultResponse(result="success").model_dump(mode="json")
|
|
|
|
|
|
@console_ns.route(
|
|
"/installed-apps/<uuid:installed_app_id>/conversations/<uuid:c_id>/unpin",
|
|
endpoint="installed_app_conversation_unpin",
|
|
)
|
|
class ConversationUnPinApi(InstalledAppResource):
|
|
@console_ns.response(200, "Success", console_ns.models[ResultResponse.__name__])
|
|
def patch(self, installed_app: InstalledApp, c_id: UUID):
|
|
app_model = installed_app.app
|
|
if app_model is None:
|
|
raise AppUnavailableError()
|
|
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)
|
|
if not isinstance(current_user, Account):
|
|
raise ValueError("current_user must be an Account instance")
|
|
WebConversationService.unpin(app_model, conversation_id, current_user)
|
|
|
|
return ResultResponse(result="success").model_dump(mode="json")
|