feat(workspace, app, agent): add duplicate name validation and restrict model/memory config on agent publish
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Annotated, Any, Dict, Optional
|
||||
from typing import Annotated, Optional
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from pydantic import Field, TypeAdapter
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import List, Optional
|
||||
import uuid
|
||||
from typing import List
|
||||
|
||||
from app.models.app_model import App
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.logging_config import get_db_logger
|
||||
from app.models.app_model import App
|
||||
|
||||
# 获取数据库专用日志器
|
||||
db_logger = get_db_logger()
|
||||
@@ -35,11 +36,27 @@ class AppRepository:
|
||||
except Exception as e:
|
||||
raise
|
||||
|
||||
def get_apps_by_name(self, app_name: str, app_type: str, workspace_id: uuid.UUID) -> List[App]:
|
||||
try:
|
||||
stmt = select(App).where(
|
||||
App.name == app_name,
|
||||
App.workspace_id == workspace_id,
|
||||
App.type == app_type,
|
||||
App.is_active.is_(True),
|
||||
)
|
||||
apps = self.db.execute(stmt).scalars().all()
|
||||
return list(apps)
|
||||
except Exception as e:
|
||||
db_logger.error(f"查询名称 {app_name} 应用异常: {str(e)}")
|
||||
raise
|
||||
|
||||
|
||||
def get_apps_by_workspace_id(db: Session, workspace_id: uuid.UUID) -> List[App]:
|
||||
"""根据工作空间ID查询应用"""
|
||||
repo = AppRepository(db)
|
||||
return repo.get_apps_by_workspace_id(workspace_id)
|
||||
|
||||
|
||||
def get_apps_by_id(db: Session, app_id: uuid.UUID) -> App:
|
||||
"""根据工作空间ID查询应用"""
|
||||
repo = AppRepository(db)
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
from sqlalchemy.orm import Session, joinedload
|
||||
from app.models.user_model import User
|
||||
from typing import List, Optional
|
||||
import uuid
|
||||
from app.models.workspace_model import Workspace, WorkspaceMember, WorkspaceRole
|
||||
from app.schemas.workspace_schema import WorkspaceCreate, WorkspaceUpdate
|
||||
from typing import List, Optional
|
||||
|
||||
from sqlalchemy.orm import Session, joinedload
|
||||
from sqlalchemy import select
|
||||
|
||||
from app.core.logging_config import get_db_logger
|
||||
from app.models.user_model import User
|
||||
from app.models.workspace_model import Workspace, WorkspaceMember, WorkspaceRole
|
||||
from app.schemas.workspace_schema import WorkspaceCreate
|
||||
|
||||
# 获取数据库专用日志器
|
||||
db_logger = get_db_logger()
|
||||
@@ -19,7 +22,7 @@ class WorkspaceRepository:
|
||||
def create_workspace(self, workspace_data: WorkspaceCreate, tenant_id: uuid.UUID) -> Workspace:
|
||||
"""创建工作空间"""
|
||||
db_logger.debug(f"创建工作空间记录: name={workspace_data.name}, tenant_id={tenant_id}")
|
||||
|
||||
|
||||
try:
|
||||
db_workspace = Workspace(
|
||||
name=workspace_data.name,
|
||||
@@ -34,7 +37,8 @@ class WorkspaceRepository:
|
||||
)
|
||||
self.db.add(db_workspace)
|
||||
self.db.flush()
|
||||
db_logger.info(f"工作空间记录创建成功: {workspace_data.name} (ID: {db_workspace.id}), storage_type: {workspace_data.storage_type}")
|
||||
db_logger.info(
|
||||
f"工作空间记录创建成功: {workspace_data.name} (ID: {db_workspace.id}), storage_type: {workspace_data.storage_type}")
|
||||
return db_workspace
|
||||
except Exception as e:
|
||||
db_logger.error(f"创建工作空间记录失败: name={workspace_data.name} - {str(e)}")
|
||||
@@ -43,7 +47,7 @@ class WorkspaceRepository:
|
||||
def get_workspace_by_id(self, workspace_id: uuid.UUID) -> Optional[Workspace]:
|
||||
"""根据ID获取工作空间"""
|
||||
db_logger.debug(f"根据ID查询工作空间: workspace_id={workspace_id}")
|
||||
|
||||
|
||||
try:
|
||||
workspace = self.db.query(Workspace).filter(Workspace.id == workspace_id).first()
|
||||
if workspace:
|
||||
@@ -65,7 +69,7 @@ class WorkspaceRepository:
|
||||
包含 llm, embedding, rerank 的字典,如果工作空间不存在则返回 None
|
||||
"""
|
||||
db_logger.debug(f"查询工作空间模型配置: workspace_id={workspace_id}")
|
||||
|
||||
|
||||
try:
|
||||
workspace = self.db.query(Workspace).filter(Workspace.id == workspace_id).first()
|
||||
if workspace:
|
||||
@@ -89,7 +93,7 @@ class WorkspaceRepository:
|
||||
def get_workspaces_by_user(self, user_id: uuid.UUID) -> List[Workspace]:
|
||||
"""获取用户参与的所有工作空间(包括用户创建的和作为成员的)"""
|
||||
db_logger.debug(f"查询用户参与的工作空间: user_id={user_id}")
|
||||
|
||||
|
||||
try:
|
||||
# 首先获取用户信息以获取 tenant_id
|
||||
from app.models.user_model import User
|
||||
@@ -97,7 +101,7 @@ class WorkspaceRepository:
|
||||
if not user:
|
||||
db_logger.warning(f"用户不存在: user_id={user_id}")
|
||||
return []
|
||||
|
||||
|
||||
if user.is_superuser:
|
||||
# 超级用户获取对应tenantid所有工作空间
|
||||
workspaces = (
|
||||
@@ -109,7 +113,7 @@ class WorkspaceRepository:
|
||||
)
|
||||
db_logger.debug(f"超用户查询所有工作空间: user_id={user_id}, 数量={len(workspaces)}")
|
||||
return workspaces
|
||||
|
||||
|
||||
# 获取用户作为成员的工作空间
|
||||
member_workspaces = (
|
||||
self.db.query(Workspace)
|
||||
@@ -120,7 +124,7 @@ class WorkspaceRepository:
|
||||
.order_by(Workspace.updated_at.desc())
|
||||
.all()
|
||||
)
|
||||
|
||||
|
||||
db_logger.debug(f"用户工作空间查询成功: user_id={user_id}, 数量={len(member_workspaces)}")
|
||||
return member_workspaces
|
||||
except Exception as e:
|
||||
@@ -130,7 +134,7 @@ class WorkspaceRepository:
|
||||
def get_workspaces_by_tenant(self, tenant_id: uuid.UUID) -> List[Workspace]:
|
||||
"""获取租户的所有工作空间"""
|
||||
db_logger.debug(f"查询租户的工作空间: tenant_id={tenant_id}")
|
||||
|
||||
|
||||
try:
|
||||
workspaces = (
|
||||
self.db.query(Workspace)
|
||||
@@ -144,14 +148,32 @@ class WorkspaceRepository:
|
||||
db_logger.error(f"查询租户工作空间失败: tenant_id={tenant_id} - {str(e)}")
|
||||
raise
|
||||
|
||||
def add_member(self, workspace_id: uuid.UUID, user_id: uuid.UUID, role: WorkspaceRole = WorkspaceRole.member) -> WorkspaceMember:
|
||||
def get_workspaces_by_name(self, tenant_id: uuid.UUID, workspace_name: str) -> List[Workspace]:
|
||||
try:
|
||||
stmt = (
|
||||
select(Workspace)
|
||||
.where(
|
||||
Workspace.tenant_id == tenant_id,
|
||||
Workspace.name == workspace_name,
|
||||
Workspace.is_active.is_(True)
|
||||
)
|
||||
)
|
||||
|
||||
workspaces = self.db.execute(stmt).scalars().all()
|
||||
return list(workspaces)
|
||||
except Exception as e:
|
||||
db_logger.error(f"查询工作空间失败: workspace_name={workspace_name} - {str(e)}")
|
||||
raise
|
||||
|
||||
def add_member(self, workspace_id: uuid.UUID, user_id: uuid.UUID,
|
||||
role: WorkspaceRole = WorkspaceRole.member) -> WorkspaceMember:
|
||||
"""添加工作空间成员"""
|
||||
db_logger.debug(f"添加工作空间成员: user_id={user_id}, workspace_id={workspace_id}, role={role}")
|
||||
|
||||
|
||||
try:
|
||||
db_member = WorkspaceMember(
|
||||
user_id=user_id,
|
||||
workspace_id=workspace_id,
|
||||
user_id=user_id,
|
||||
workspace_id=workspace_id,
|
||||
role=role
|
||||
)
|
||||
self.db.add(db_member)
|
||||
@@ -165,7 +187,7 @@ class WorkspaceRepository:
|
||||
def get_member(self, user_id: uuid.UUID, workspace_id: uuid.UUID) -> Optional[WorkspaceMember]:
|
||||
"""获取工作空间成员"""
|
||||
db_logger.debug(f"查询工作空间成员: user_id={user_id}, workspace_id={workspace_id}")
|
||||
|
||||
|
||||
try:
|
||||
member = self.db.query(WorkspaceMember).filter(
|
||||
WorkspaceMember.user_id == user_id,
|
||||
@@ -173,7 +195,8 @@ class WorkspaceRepository:
|
||||
WorkspaceMember.is_active.is_(True),
|
||||
).first()
|
||||
if member:
|
||||
db_logger.debug(f"工作空间成员查询成功: user_id={user_id}, workspace_id={workspace_id}, role={member.role}")
|
||||
db_logger.debug(
|
||||
f"工作空间成员查询成功: user_id={user_id}, workspace_id={workspace_id}, role={member.role}")
|
||||
else:
|
||||
db_logger.debug(f"工作空间成员不存在: user_id={user_id}, workspace_id={workspace_id}")
|
||||
return member
|
||||
@@ -199,7 +222,7 @@ class WorkspaceRepository:
|
||||
except Exception as e:
|
||||
db_logger.error(f"查询成员列表失败: workspace_id={workspace_id} - {str(e)}")
|
||||
raise
|
||||
|
||||
|
||||
def get_member_by_id(self, member_id: uuid.UUID) -> WorkspaceMember:
|
||||
"""按成员ID获取工作空间成员,并预加载 user 与 workspace 关系"""
|
||||
db_logger.debug(f"查询成员的工作空间: member_id={member_id}")
|
||||
@@ -214,7 +237,8 @@ class WorkspaceRepository:
|
||||
.first()
|
||||
)
|
||||
if member:
|
||||
db_logger.debug(f"成员查询成功: member_id={member_id}, workspace_id={member.workspace_id}, role={member.role}")
|
||||
db_logger.debug(
|
||||
f"成员查询成功: member_id={member_id}, workspace_id={member.workspace_id}, role={member.role}")
|
||||
else:
|
||||
db_logger.debug(f"成员不存在: member_id={member_id}")
|
||||
return member
|
||||
@@ -222,7 +246,8 @@ class WorkspaceRepository:
|
||||
db_logger.error(f"查询成员列表失败: member_id={member_id} - {str(e)}")
|
||||
raise
|
||||
|
||||
def update_member_role(self, workspace_id: uuid.UUID, user_id: uuid.UUID, role: WorkspaceRole) -> Optional[WorkspaceMember]:
|
||||
def update_member_role(self, workspace_id: uuid.UUID, user_id: uuid.UUID, role: WorkspaceRole) -> Optional[
|
||||
WorkspaceMember]:
|
||||
try:
|
||||
member = self.db.query(WorkspaceMember).filter(
|
||||
WorkspaceMember.workspace_id == workspace_id,
|
||||
@@ -255,7 +280,7 @@ class WorkspaceRepository:
|
||||
except Exception as e:
|
||||
db_logger.error(f"删除成员失败: workspace_id={workspace_id}, user_id={user_id} - {str(e)}")
|
||||
raise
|
||||
|
||||
|
||||
def delete_member_by_id(self, member_id: uuid.UUID) -> Optional[WorkspaceMember]:
|
||||
try:
|
||||
member = self.db.query(WorkspaceMember).filter(
|
||||
@@ -271,7 +296,7 @@ class WorkspaceRepository:
|
||||
except Exception as e:
|
||||
db_logger.error(f"删除成员失败: id={member_id} - {str(e)}")
|
||||
raise
|
||||
|
||||
|
||||
def update_member_role_by_id(self, id: uuid.UUID, role: WorkspaceRole) -> Optional[WorkspaceMember]:
|
||||
try:
|
||||
member = self.db.query(WorkspaceMember).filter(
|
||||
@@ -288,12 +313,18 @@ class WorkspaceRepository:
|
||||
db_logger.error(f"更新成员角色失败: id={id} - {str(e)}")
|
||||
raise
|
||||
|
||||
|
||||
# 保持向后兼容的函数
|
||||
def get_workspace_by_id(db: Session, workspace_id: uuid.UUID) -> Workspace | None:
|
||||
repo = WorkspaceRepository(db)
|
||||
return repo.get_workspace_by_id(workspace_id)
|
||||
|
||||
|
||||
def get_workspaces_by_name(db: Session, tenant_id: uuid.UUID, name: str) -> List[Workspace]:
|
||||
repo = WorkspaceRepository(db)
|
||||
return repo.get_workspaces_by_name(tenant_id, name)
|
||||
|
||||
|
||||
def get_workspaces_by_user(db: Session, user_id: uuid.UUID) -> List[Workspace]:
|
||||
repo = WorkspaceRepository(db)
|
||||
return repo.get_workspaces_by_user(user_id)
|
||||
@@ -315,7 +346,7 @@ def create_workspace(db: Session, workspace: WorkspaceCreate, tenant_id: uuid.UU
|
||||
|
||||
|
||||
def add_member_to_workspace(
|
||||
db: Session, user_id: uuid.UUID, workspace_id: uuid.UUID, role: WorkspaceRole
|
||||
db: Session, user_id: uuid.UUID, workspace_id: uuid.UUID, role: WorkspaceRole
|
||||
) -> WorkspaceMember:
|
||||
repo = WorkspaceRepository(db)
|
||||
return repo.add_member(workspace_id, user_id, role)
|
||||
@@ -325,39 +356,43 @@ def get_members_by_workspace(db: Session, workspace_id: uuid.UUID) -> List[Works
|
||||
repo = WorkspaceRepository(db)
|
||||
return repo.get_members_by_workspace(workspace_id)
|
||||
|
||||
|
||||
def get_member_by_id(db: Session, member_id: uuid.UUID) -> WorkspaceMember | None:
|
||||
repo = WorkspaceRepository(db)
|
||||
return repo.get_member_by_id(member_id)
|
||||
|
||||
|
||||
def update_member_role_in_workspace(
|
||||
db: Session,
|
||||
user_id: uuid.UUID,
|
||||
workspace_id: uuid.UUID,
|
||||
role: WorkspaceRole,
|
||||
db: Session,
|
||||
user_id: uuid.UUID,
|
||||
workspace_id: uuid.UUID,
|
||||
role: WorkspaceRole,
|
||||
) -> Optional[WorkspaceMember]:
|
||||
repo = WorkspaceRepository(db)
|
||||
return repo.update_member_role(workspace_id, user_id, role)
|
||||
|
||||
|
||||
def remove_member_from_workspace(
|
||||
db: Session,
|
||||
user_id: uuid.UUID,
|
||||
workspace_id: uuid.UUID,
|
||||
db: Session,
|
||||
user_id: uuid.UUID,
|
||||
workspace_id: uuid.UUID,
|
||||
) -> Optional[WorkspaceMember]:
|
||||
repo = WorkspaceRepository(db)
|
||||
return repo.deactivate_member(workspace_id, user_id)
|
||||
|
||||
|
||||
def remove_member_from_workspace_by_id(
|
||||
db: Session,
|
||||
member_id: uuid.UUID,
|
||||
db: Session,
|
||||
member_id: uuid.UUID,
|
||||
) -> Optional[WorkspaceMember]:
|
||||
repo = WorkspaceRepository(db)
|
||||
return repo.delete_member_by_id(member_id)
|
||||
|
||||
|
||||
def update_member_role_by_id(
|
||||
db: Session,
|
||||
id: uuid.UUID,
|
||||
role: WorkspaceRole,
|
||||
db: Session,
|
||||
id: uuid.UUID,
|
||||
role: WorkspaceRole,
|
||||
) -> Optional[WorkspaceMember]:
|
||||
repo = WorkspaceRepository(db)
|
||||
return repo.update_member_role_by_id(id, role)
|
||||
|
||||
@@ -33,7 +33,7 @@ from app.models import (
|
||||
Workspace,
|
||||
)
|
||||
from app.models.app_model import AppStatus, AppType
|
||||
from app.repositories.app_repository import get_apps_by_id
|
||||
from app.repositories.app_repository import get_apps_by_id, AppRepository
|
||||
from app.repositories.workflow_repository import WorkflowConfigRepository
|
||||
from app.schemas import app_schema
|
||||
from app.schemas.workflow_schema import WorkflowConfigUpdate
|
||||
@@ -59,6 +59,7 @@ class AppService:
|
||||
db: 数据库会话
|
||||
"""
|
||||
self.db = db
|
||||
self.app_repo = AppRepository(self.db)
|
||||
|
||||
# ==================== 私有辅助方法 ====================
|
||||
|
||||
@@ -521,6 +522,9 @@ class AppService:
|
||||
"创建应用",
|
||||
extra={"app_name": data.name, "type": data.type, "workspace_id": str(workspace_id)}
|
||||
)
|
||||
apps = self.app_repo.get_apps_by_name(data.name, data.type, workspace_id)
|
||||
if apps:
|
||||
raise BusinessException(message="已存在同名应用", code=BizCode.RESOURCE_ALREADY_EXISTS)
|
||||
|
||||
try:
|
||||
now = datetime.datetime.now()
|
||||
@@ -1368,6 +1372,15 @@ class AppService:
|
||||
if not agent_cfg:
|
||||
raise BusinessException("Agent 应用缺少配置,无法发布", BizCode.AGENT_CONFIG_MISSING)
|
||||
|
||||
miss_params = []
|
||||
if agent_cfg.default_model_config_id is None:
|
||||
miss_params.append("model config")
|
||||
|
||||
if agent_cfg.memory.get("enabled") and not agent_cfg.memory.get("memory_config_id"):
|
||||
miss_params.append("memory config")
|
||||
if miss_params:
|
||||
raise BusinessException(f"{', '.join(miss_params)} is required")
|
||||
|
||||
config = {
|
||||
"system_prompt": agent_cfg.system_prompt,
|
||||
"model_parameters": model_parameters_to_dict(agent_cfg.model_parameters),
|
||||
|
||||
@@ -2,11 +2,11 @@ import datetime
|
||||
import hashlib
|
||||
import secrets
|
||||
import uuid
|
||||
from os import getenv
|
||||
from typing import List, Optional
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.config.default_ontology_initializer import DefaultOntologyInitializer
|
||||
from app.core.config import settings
|
||||
from app.core.error_codes import BizCode
|
||||
from app.core.exceptions import BusinessException, PermissionDeniedException
|
||||
@@ -30,17 +30,15 @@ from app.schemas.workspace_schema import (
|
||||
WorkspaceModelsUpdate,
|
||||
WorkspaceUpdate,
|
||||
)
|
||||
from app.config.default_ontology_initializer import DefaultOntologyInitializer
|
||||
|
||||
# 获取业务逻辑专用日志器
|
||||
business_logger = get_business_logger()
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
def switch_workspace(
|
||||
db: Session,
|
||||
workspace_id: uuid.UUID,
|
||||
user: User,
|
||||
db: Session,
|
||||
workspace_id: uuid.UUID,
|
||||
user: User,
|
||||
):
|
||||
"""切换工作空间"""
|
||||
business_logger.debug(f"用户 {user.username} 请求切换工作空间为 {workspace_id}")
|
||||
@@ -60,31 +58,32 @@ def switch_workspace(
|
||||
raise BusinessException(f"切换工作空间失败: {str(e)}", BizCode.INTERNAL_ERROR)
|
||||
|
||||
|
||||
def delete_workspace_member(
|
||||
db: Session,
|
||||
workspace_id: uuid.UUID,
|
||||
member_id: uuid.UUID,
|
||||
user: User,
|
||||
):
|
||||
"""删除工作空间成员"""
|
||||
business_logger.debug(f"用户 {user.username} 请求删除工作空间 {workspace_id} 的成员 {member_id}")
|
||||
_check_workspace_admin_permission(db, workspace_id, user)
|
||||
workspace_member = workspace_repository.get_member_by_id(db=db, member_id=member_id)
|
||||
if not workspace_member:
|
||||
raise BusinessException(f"工作空间成员 {member_id} 不存在", BizCode.WORKSPACE_NOT_FOUND)
|
||||
def delete_workspace_member(
|
||||
db: Session,
|
||||
workspace_id: uuid.UUID,
|
||||
member_id: uuid.UUID,
|
||||
user: User,
|
||||
):
|
||||
"""删除工作空间成员"""
|
||||
business_logger.debug(f"用户 {user.username} 请求删除工作空间 {workspace_id} 的成员 {member_id}")
|
||||
_check_workspace_admin_permission(db, workspace_id, user)
|
||||
workspace_member = workspace_repository.get_member_by_id(db=db, member_id=member_id)
|
||||
if not workspace_member:
|
||||
raise BusinessException(f"工作空间成员 {member_id} 不存在", BizCode.WORKSPACE_NOT_FOUND)
|
||||
|
||||
if workspace_member.workspace_id != workspace_id:
|
||||
raise BusinessException(f"工作空间成员 {member_id} 不存在于工作空间 {workspace_id}", BizCode.WORKSPACE_NOT_FOUND)
|
||||
if workspace_member.workspace_id != workspace_id:
|
||||
raise BusinessException(f"工作空间成员 {member_id} 不存在于工作空间 {workspace_id}",
|
||||
BizCode.WORKSPACE_NOT_FOUND)
|
||||
|
||||
try:
|
||||
workspace_member.is_active = False
|
||||
workspace_member.user.current_workspace_id = None
|
||||
db.commit()
|
||||
business_logger.info(f"用户 {user.username} 成功删除工作空间 {workspace_id} 的成员 {member_id}")
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
business_logger.error(f"删除工作空间成员失败 - 工作空间: {workspace_id}, 成员: {member_id}, 错误: {str(e)}")
|
||||
raise BusinessException(f"删除工作空间成员失败: {str(e)}", BizCode.INTERNAL_ERROR)
|
||||
try:
|
||||
workspace_member.is_active = False
|
||||
workspace_member.user.current_workspace_id = None
|
||||
db.commit()
|
||||
business_logger.info(f"用户 {user.username} 成功删除工作空间 {workspace_id} 的成员 {member_id}")
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
business_logger.error(f"删除工作空间成员失败 - 工作空间: {workspace_id}, 成员: {member_id}, 错误: {str(e)}")
|
||||
raise BusinessException(f"删除工作空间成员失败: {str(e)}", BizCode.INTERNAL_ERROR)
|
||||
|
||||
|
||||
def get_user_workspaces(db: Session, user: User) -> List[Workspace]:
|
||||
@@ -102,19 +101,19 @@ def get_user_workspaces(db: Session, user: User) -> List[Workspace]:
|
||||
"""
|
||||
business_logger.debug(f"获取用户工作空间列表: {user.username} (ID: {user.id})")
|
||||
workspaces = workspace_repository.get_workspaces_by_user(db=db, user_id=user.id)
|
||||
|
||||
|
||||
# Ensure each neo4j workspace has a default memory config
|
||||
for workspace in workspaces:
|
||||
if workspace.storage_type == 'neo4j':
|
||||
_ensure_default_memory_config(db, workspace)
|
||||
_ensure_default_ontology_scenes(db, workspace)
|
||||
|
||||
|
||||
business_logger.info(f"用户 {user.username} 的工作空间数量: {len(workspaces)}")
|
||||
return workspaces
|
||||
|
||||
|
||||
def _create_workspace_only(
|
||||
db: Session, workspace: WorkspaceCreate, owner: User
|
||||
db: Session, workspace: WorkspaceCreate, owner: User
|
||||
) -> Workspace:
|
||||
business_logger.debug(f"创建工作空间: {workspace.name}, 创建者: {owner.username}")
|
||||
|
||||
@@ -138,9 +137,14 @@ def create_workspace(
|
||||
f"创建工作空间: {workspace.name}, 创建者: {user.username}, "
|
||||
f"storage_type: {workspace.storage_type}"
|
||||
)
|
||||
llm=workspace.llm
|
||||
embedding=workspace.embedding
|
||||
rerank=workspace.rerank
|
||||
if workspace_repository.get_workspaces_by_name(db=db, name=workspace.name, tenant_id=user.tenant_id):
|
||||
raise BusinessException(
|
||||
message="同名工作空间已存在",
|
||||
code=BizCode.RESOURCE_ALREADY_EXISTS
|
||||
)
|
||||
llm = workspace.llm
|
||||
embedding = workspace.embedding
|
||||
rerank = workspace.rerank
|
||||
try:
|
||||
# Create the workspace without adding any members
|
||||
business_logger.debug(f"创建工作空间: {workspace.name}")
|
||||
@@ -159,26 +163,26 @@ def create_workspace(
|
||||
success, error_msg = initializer.initialize_default_scenes(
|
||||
db_workspace.id, language=language
|
||||
)
|
||||
|
||||
|
||||
if success:
|
||||
business_logger.info(
|
||||
f"为工作空间 {db_workspace.id} 创建默认本体场景成功 (language={language})"
|
||||
)
|
||||
|
||||
# 获取默认场景ID,优先使用"在线教育"场景,如果不存在则使用"情感陪伴"场景
|
||||
|
||||
# 获取默认场景ID,优先使用"在线教育"场景,如果不存在则使用"情感陪伴"场景
|
||||
from app.repositories.ontology_scene_repository import OntologySceneRepository
|
||||
from app.config.default_ontology_config import (
|
||||
ONLINE_EDUCATION_SCENE,
|
||||
ONLINE_EDUCATION_SCENE,
|
||||
EMOTIONAL_COMPANION_SCENE,
|
||||
get_scene_name
|
||||
)
|
||||
|
||||
|
||||
scene_repo = OntologySceneRepository(db)
|
||||
|
||||
|
||||
# 优先尝试获取教育场景
|
||||
education_scene_name = get_scene_name(ONLINE_EDUCATION_SCENE, language)
|
||||
education_scene = scene_repo.get_by_name(education_scene_name, db_workspace.id)
|
||||
|
||||
|
||||
if education_scene:
|
||||
default_scene_id = education_scene.scene_id
|
||||
default_scene_name = education_scene.scene_name
|
||||
@@ -189,7 +193,7 @@ def create_workspace(
|
||||
# 如果教育场景不存在,尝试获取情感陪伴场景
|
||||
companion_scene_name = get_scene_name(EMOTIONAL_COMPANION_SCENE, language)
|
||||
companion_scene = scene_repo.get_by_name(companion_scene_name, db_workspace.id)
|
||||
|
||||
|
||||
if companion_scene:
|
||||
default_scene_id = companion_scene.scene_id
|
||||
default_scene_name = companion_scene.scene_name
|
||||
@@ -256,10 +260,10 @@ def create_workspace(
|
||||
avatar='',
|
||||
type=KnowledgeType.General,
|
||||
permission_id=PermissionType.Memory,
|
||||
embedding_id=uuid.UUID(getenv('KB_embedding_id')) if None else embedding,
|
||||
reranker_id=uuid.UUID(getenv('KB_reranker_id')) if None else rerank,
|
||||
llm_id=uuid.UUID(getenv('KB_llm_id')) if None else llm,
|
||||
image2text_id=uuid.UUID(getenv('KB_llm_id')) if None else llm,
|
||||
embedding_id=embedding,
|
||||
reranker_id=rerank,
|
||||
llm_id=llm,
|
||||
image2text_id=llm,
|
||||
parser_config={
|
||||
"layout_recognize": "DeepDOC",
|
||||
"chunk_token_num": 256,
|
||||
@@ -294,7 +298,7 @@ def create_workspace(
|
||||
business_logger.info(
|
||||
f"工作空间 {db_workspace.id} 及相关资源创建完成并已提交"
|
||||
)
|
||||
|
||||
|
||||
return db_workspace
|
||||
|
||||
except Exception as e:
|
||||
@@ -304,11 +308,11 @@ def create_workspace(
|
||||
|
||||
|
||||
def update_workspace(
|
||||
db: Session, workspace_id: uuid.UUID, workspace_in: WorkspaceUpdate, user: User
|
||||
db: Session, workspace_id: uuid.UUID, workspace_in: WorkspaceUpdate, user: User
|
||||
) -> Workspace:
|
||||
business_logger.info(f"更新工作空间: workspace_id={workspace_id}, 操作者: {user.username}")
|
||||
|
||||
db_workspace = _check_workspace_admin_permission(db,workspace_id,user)
|
||||
db_workspace = _check_workspace_admin_permission(db, workspace_id, user)
|
||||
try:
|
||||
# 更新工作空间
|
||||
business_logger.debug(f"执行工作空间更新: {db_workspace.name} (ID: {workspace_id})")
|
||||
@@ -328,7 +332,7 @@ def update_workspace(
|
||||
|
||||
|
||||
def get_workspace_members(
|
||||
db: Session, workspace_id: uuid.UUID, user: User
|
||||
db: Session, workspace_id: uuid.UUID, user: User
|
||||
) -> List[WorkspaceMember]:
|
||||
"""获取某工作空间的成员列表(关系序列化由模型关系支持)"""
|
||||
business_logger.info(f"获取工作空间成员: workspace_id={workspace_id}, 操作者: {user.username}")
|
||||
@@ -372,7 +376,6 @@ def get_workspace_members(
|
||||
return members
|
||||
|
||||
|
||||
|
||||
# ==================== 邀请相关服务方法 ====================
|
||||
|
||||
def _generate_invite_token() -> tuple[str, str]:
|
||||
@@ -465,13 +468,14 @@ def _check_workspace_admin_permission(db: Session, workspace_id: uuid.UUID, user
|
||||
|
||||
|
||||
def create_workspace_invite(
|
||||
db: Session,
|
||||
workspace_id: uuid.UUID,
|
||||
invite_data: WorkspaceInviteCreate,
|
||||
user: User
|
||||
db: Session,
|
||||
workspace_id: uuid.UUID,
|
||||
invite_data: WorkspaceInviteCreate,
|
||||
user: User
|
||||
) -> WorkspaceInviteResponse:
|
||||
"""创建工作空间邀请"""
|
||||
business_logger.info(f"创建工作空间邀请: workspace_id={workspace_id}, email={invite_data.email}, 创建者: {user.username}")
|
||||
business_logger.info(
|
||||
f"创建工作空间邀请: workspace_id={workspace_id}, email={invite_data.email}, 创建者: {user.username}")
|
||||
|
||||
try:
|
||||
# 检查权限
|
||||
@@ -534,17 +538,18 @@ def create_workspace_invite(
|
||||
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
business_logger.error(f"创建工作空间邀请失败: workspace_id={workspace_id}, email={invite_data.email} - {str(e)}")
|
||||
business_logger.error(
|
||||
f"创建工作空间邀请失败: workspace_id={workspace_id}, email={invite_data.email} - {str(e)}")
|
||||
raise
|
||||
|
||||
|
||||
def get_workspace_invites(
|
||||
db: Session,
|
||||
workspace_id: uuid.UUID,
|
||||
user: User,
|
||||
status: Optional[InviteStatus] = None,
|
||||
limit: int = 50,
|
||||
offset: int = 0
|
||||
db: Session,
|
||||
workspace_id: uuid.UUID,
|
||||
user: User,
|
||||
status: Optional[InviteStatus] = None,
|
||||
limit: int = 50,
|
||||
offset: int = 0
|
||||
) -> List[WorkspaceInviteResponse]:
|
||||
"""获取工作空间邀请列表"""
|
||||
business_logger.info(f"获取工作空间邀请列表: workspace_id={workspace_id}, 操作者: {user.username}")
|
||||
@@ -605,9 +610,9 @@ def validate_invite_token(db: Session, token: str) -> InviteValidateResponse:
|
||||
|
||||
|
||||
def accept_workspace_invite(
|
||||
db: Session,
|
||||
accept_request: InviteAcceptRequest,
|
||||
user: User
|
||||
db: Session,
|
||||
accept_request: InviteAcceptRequest,
|
||||
user: User
|
||||
) -> dict:
|
||||
"""接受工作空间邀请"""
|
||||
business_logger.info(f"接受工作空间邀请: 用户 {user.username}")
|
||||
@@ -695,7 +700,8 @@ def accept_workspace_invite(
|
||||
# 获取工作空间信息
|
||||
workspace = workspace_repository.get_workspace_by_id(db=db, workspace_id=invite.workspace_id)
|
||||
|
||||
business_logger.info(f"用户成功加入工作空间: user={user.username}, workspace={workspace.name}, role={workspace_role}")
|
||||
business_logger.info(
|
||||
f"用户成功加入工作空间: user={user.username}, workspace={workspace.name}, role={workspace_role}")
|
||||
|
||||
return {
|
||||
"message": "Successfully joined the workspace",
|
||||
@@ -710,13 +716,14 @@ def accept_workspace_invite(
|
||||
|
||||
|
||||
def revoke_workspace_invite(
|
||||
db: Session,
|
||||
workspace_id: uuid.UUID,
|
||||
invite_id: uuid.UUID,
|
||||
user: User
|
||||
db: Session,
|
||||
workspace_id: uuid.UUID,
|
||||
invite_id: uuid.UUID,
|
||||
user: User
|
||||
) -> dict:
|
||||
"""撤销工作空间邀请"""
|
||||
business_logger.info(f"撤销工作空间邀请: workspace_id={workspace_id}, invite_id={invite_id}, 操作者: {user.username}")
|
||||
business_logger.info(
|
||||
f"撤销工作空间邀请: workspace_id={workspace_id}, invite_id={invite_id}, 操作者: {user.username}")
|
||||
|
||||
try:
|
||||
# 检查权限
|
||||
@@ -745,13 +752,14 @@ def revoke_workspace_invite(
|
||||
|
||||
|
||||
def update_workspace_member_roles(
|
||||
db: Session,
|
||||
workspace_id: uuid.UUID,
|
||||
updates: List[WorkspaceMemberUpdate],
|
||||
user: User,
|
||||
db: Session,
|
||||
workspace_id: uuid.UUID,
|
||||
updates: List[WorkspaceMemberUpdate],
|
||||
user: User,
|
||||
) -> List[WorkspaceMember]:
|
||||
"""更新工作空间成员角色"""
|
||||
business_logger.info(f"更新工作空间成员角色: workspace_id={workspace_id}, 操作者: {user.username}, 更新数量: {len(updates)}")
|
||||
business_logger.info(
|
||||
f"更新工作空间成员角色: workspace_id={workspace_id}, 操作者: {user.username}, 更新数量: {len(updates)}")
|
||||
|
||||
# 检查管理员权限
|
||||
_check_workspace_admin_permission(db, workspace_id, user)
|
||||
@@ -765,7 +773,8 @@ def update_workspace_member_roles(
|
||||
for upd in updates:
|
||||
# 检查成员是否存在
|
||||
if upd.id not in member_map:
|
||||
raise BusinessException(f"成员 {upd.id} 不存在于工作空间 {workspace_id}", BizCode.WORKSPACE_MEMBER_NOT_FOUND)
|
||||
raise BusinessException(f"成员 {upd.id} 不存在于工作空间 {workspace_id}",
|
||||
BizCode.WORKSPACE_MEMBER_NOT_FOUND)
|
||||
|
||||
member = member_map[upd.id]
|
||||
|
||||
@@ -917,10 +926,10 @@ def get_workspace_models_configs(
|
||||
|
||||
|
||||
def update_workspace_models_configs(
|
||||
db: Session,
|
||||
workspace_id: uuid.UUID,
|
||||
models_update: WorkspaceModelsUpdate,
|
||||
user: User,
|
||||
db: Session,
|
||||
workspace_id: uuid.UUID,
|
||||
models_update: WorkspaceModelsUpdate,
|
||||
user: User,
|
||||
) -> Workspace:
|
||||
"""更新工作空间的模型配置(llm, embedding, rerank)
|
||||
|
||||
@@ -968,8 +977,8 @@ def update_workspace_models_configs(
|
||||
|
||||
|
||||
def _fill_workspace_configs_model_defaults(
|
||||
db: Session,
|
||||
workspace: Workspace
|
||||
db: Session,
|
||||
workspace: Workspace
|
||||
) -> None:
|
||||
"""Fill empty model fields for all memory configs in a workspace.
|
||||
|
||||
@@ -981,43 +990,43 @@ def _fill_workspace_configs_model_defaults(
|
||||
workspace: The workspace containing default model settings
|
||||
"""
|
||||
from app.models.memory_config_model import MemoryConfig
|
||||
|
||||
|
||||
# Get all configs for this workspace
|
||||
configs = db.query(MemoryConfig).filter(
|
||||
MemoryConfig.workspace_id == workspace.id
|
||||
).all()
|
||||
|
||||
|
||||
if not configs:
|
||||
return
|
||||
|
||||
|
||||
# Map of memory_config field -> workspace field
|
||||
model_field_mappings = [
|
||||
("llm_id", "llm"),
|
||||
("embedding_id", "embedding"),
|
||||
("rerank_id", "rerank"),
|
||||
("reflection_model_id", "llm"), # reflection uses LLM
|
||||
("emotion_model_id", "llm"), # emotion uses LLM
|
||||
("emotion_model_id", "llm"), # emotion uses LLM
|
||||
]
|
||||
|
||||
|
||||
configs_updated = 0
|
||||
|
||||
|
||||
for memory_config in configs:
|
||||
updated_fields = []
|
||||
|
||||
|
||||
for config_field, workspace_field in model_field_mappings:
|
||||
config_value = getattr(memory_config, config_field, None)
|
||||
workspace_value = getattr(workspace, workspace_field, None)
|
||||
|
||||
|
||||
if not config_value and workspace_value:
|
||||
setattr(memory_config, config_field, workspace_value)
|
||||
updated_fields.append(config_field)
|
||||
|
||||
|
||||
if updated_fields:
|
||||
configs_updated += 1
|
||||
business_logger.debug(
|
||||
f"Updated memory config {memory_config.config_id} fields: {updated_fields}"
|
||||
)
|
||||
|
||||
|
||||
if configs_updated > 0:
|
||||
try:
|
||||
db.commit()
|
||||
@@ -1032,14 +1041,14 @@ def _fill_workspace_configs_model_defaults(
|
||||
|
||||
|
||||
def _create_default_memory_config(
|
||||
db: Session,
|
||||
workspace_id: uuid.UUID,
|
||||
workspace_name: str,
|
||||
llm_id: Optional[uuid.UUID] = None,
|
||||
embedding_id: Optional[uuid.UUID] = None,
|
||||
rerank_id: Optional[uuid.UUID] = None,
|
||||
scene_id: Optional[uuid.UUID] = None,
|
||||
pruning_scene_name: Optional[str] = None,
|
||||
db: Session,
|
||||
workspace_id: uuid.UUID,
|
||||
workspace_name: str,
|
||||
llm_id: Optional[uuid.UUID] = None,
|
||||
embedding_id: Optional[uuid.UUID] = None,
|
||||
rerank_id: Optional[uuid.UUID] = None,
|
||||
scene_id: Optional[uuid.UUID] = None,
|
||||
pruning_scene_name: Optional[str] = None,
|
||||
) -> None:
|
||||
"""Create a default memory config for a newly created workspace.
|
||||
|
||||
@@ -1054,9 +1063,9 @@ def _create_default_memory_config(
|
||||
pruning_scene_name: Optional pruning scene name,取自 ontology_scene.scene_name
|
||||
"""
|
||||
from app.models.memory_config_model import MemoryConfig
|
||||
|
||||
|
||||
config_id = uuid.uuid4()
|
||||
|
||||
|
||||
default_config = MemoryConfig(
|
||||
config_id=config_id,
|
||||
config_name=f"{workspace_name} 默认配置",
|
||||
@@ -1070,10 +1079,10 @@ def _create_default_memory_config(
|
||||
state=True, # Active by default
|
||||
is_default=True, # Mark as workspace default
|
||||
)
|
||||
|
||||
|
||||
db.add(default_config)
|
||||
db.flush() # 使用 flush 而不是 commit,让调用者统一提交
|
||||
|
||||
|
||||
business_logger.info(
|
||||
"Created default memory config for workspace",
|
||||
extra={
|
||||
@@ -1084,6 +1093,7 @@ def _create_default_memory_config(
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
# ==================== 检查配置相关服务 ====================
|
||||
|
||||
def _ensure_default_memory_config(db: Session, workspace: Workspace) -> None:
|
||||
@@ -1096,19 +1106,19 @@ def _ensure_default_memory_config(db: Session, workspace: Workspace) -> None:
|
||||
workspace: The workspace to check
|
||||
"""
|
||||
from app.models.memory_config_model import MemoryConfig
|
||||
|
||||
|
||||
# Check if default config exists for this workspace
|
||||
existing_default = db.query(MemoryConfig).filter(
|
||||
MemoryConfig.workspace_id == workspace.id,
|
||||
MemoryConfig.is_default == True
|
||||
).first()
|
||||
|
||||
|
||||
if not existing_default:
|
||||
# No default config exists, create one
|
||||
business_logger.info(
|
||||
f"Workspace {workspace.id} missing default memory config, creating one"
|
||||
)
|
||||
|
||||
|
||||
# 尝试获取默认场景ID,优先教育场景,其次情感陪伴场景
|
||||
default_scene_id = None
|
||||
try:
|
||||
@@ -1118,7 +1128,7 @@ def _ensure_default_memory_config(db: Session, workspace: Workspace) -> None:
|
||||
EMOTIONAL_COMPANION_SCENE,
|
||||
get_scene_name
|
||||
)
|
||||
|
||||
|
||||
scene_repo = OntologySceneRepository(db)
|
||||
# 尝试中文和英文场景名称
|
||||
for language in ["zh", "en"]:
|
||||
@@ -1131,7 +1141,7 @@ def _ensure_default_memory_config(db: Session, workspace: Workspace) -> None:
|
||||
f"找到教育场景用于默认记忆配置: scene_id={default_scene_id}, scene_name={education_scene_name}"
|
||||
)
|
||||
break
|
||||
|
||||
|
||||
# 如果教育场景不存在,尝试情感陪伴场景
|
||||
companion_scene_name = get_scene_name(EMOTIONAL_COMPANION_SCENE, language)
|
||||
companion_scene = scene_repo.get_by_name(companion_scene_name, workspace.id)
|
||||
@@ -1145,7 +1155,7 @@ def _ensure_default_memory_config(db: Session, workspace: Workspace) -> None:
|
||||
business_logger.warning(
|
||||
f"获取默认场景失败,将创建不关联场景的记忆配置: {str(scene_error)}"
|
||||
)
|
||||
|
||||
|
||||
try:
|
||||
_create_default_memory_config(
|
||||
db=db,
|
||||
@@ -1160,7 +1170,7 @@ def _ensure_default_memory_config(db: Session, workspace: Workspace) -> None:
|
||||
business_logger.error(
|
||||
f"Failed to create default memory config for workspace {workspace.id}: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
# Fill empty model fields for ALL configs in this workspace
|
||||
_fill_workspace_configs_model_defaults(db, workspace)
|
||||
|
||||
@@ -1209,4 +1219,3 @@ def _ensure_default_ontology_scenes(db: Session, workspace: Workspace) -> None:
|
||||
business_logger.error(
|
||||
f"为工作空间 {workspace.id} 补建默认本体场景异常: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user