Merge pull request #476 from SuanmoSuanyangTechnology/fix/ontology
Fix/ontology
This commit is contained in:
@@ -2,7 +2,7 @@ from typing import Optional
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from fastapi.responses import StreamingResponse
|
||||
from fastapi.responses import StreamingResponse, JSONResponse
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.error_codes import BizCode
|
||||
@@ -85,6 +85,7 @@ def create_config(
|
||||
payload: ConfigParamsCreate,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
x_language_type: Optional[str] = Header(None, alias="X-Language-Type"),
|
||||
) -> dict:
|
||||
workspace_id = current_user.current_workspace_id
|
||||
# 检查用户是否已选择工作空间
|
||||
@@ -100,6 +101,15 @@ def create_config(
|
||||
result = svc.create(payload)
|
||||
return success(data=result, msg="创建成功")
|
||||
except Exception as e:
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
if isinstance(e, IntegrityError) and "uq_workspace_config_name" in str(getattr(e, 'orig', '')):
|
||||
api_logger.warning(f"重复的配置名称 '{payload.config_name}' 在工作空间 {workspace_id}")
|
||||
lang = get_language_from_header(x_language_type)
|
||||
if lang == "en":
|
||||
msg = fail(BizCode.BAD_REQUEST, "Config name already exists", f"A config named \"{payload.config_name}\" already exists in the current workspace. Please use a different name.")
|
||||
else:
|
||||
msg = fail(BizCode.BAD_REQUEST, "配置名称已存在", f"当前工作空间下已存在名为「{payload.config_name}」的记忆配置,请使用其他名称")
|
||||
return JSONResponse(status_code=400, content=msg)
|
||||
api_logger.error(f"Create config failed: {str(e)}")
|
||||
return fail(BizCode.INTERNAL_ERROR, "创建配置失败", str(e))
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ from typing import Dict, Optional, List
|
||||
from urllib.parse import quote
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, File, UploadFile, Form, Header
|
||||
from fastapi.responses import StreamingResponse
|
||||
from fastapi.responses import StreamingResponse, JSONResponse
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.config import settings
|
||||
@@ -289,7 +289,8 @@ async def extract_ontology(
|
||||
async def create_scene(
|
||||
request: SceneCreateRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
current_user: User = Depends(get_current_user),
|
||||
x_language_type: Optional[str] = Header(None, alias="X-Language-Type")
|
||||
):
|
||||
"""创建本体场景
|
||||
|
||||
@@ -360,8 +361,18 @@ async def create_scene(
|
||||
return fail(BizCode.BAD_REQUEST, "请求参数无效", str(e))
|
||||
|
||||
except RuntimeError as e:
|
||||
api_logger.error(f"Runtime error in scene creation: {str(e)}", exc_info=True)
|
||||
return fail(BizCode.INTERNAL_ERROR, "场景创建失败", str(e))
|
||||
err_str = str(e)
|
||||
if "UniqueViolation" in err_str or "uq_workspace_scene_name" in err_str:
|
||||
api_logger.warning(f"Duplicate scene name '{request.scene_name}' in workspace {current_user.current_workspace_id}")
|
||||
from app.core.language_utils import get_language_from_header
|
||||
lang = get_language_from_header(x_language_type)
|
||||
if lang == "en":
|
||||
msg = fail(BizCode.BAD_REQUEST, "Scene name already exists", f"A scene named \"{request.scene_name}\" already exists in the current workspace. Please use a different name.")
|
||||
else:
|
||||
msg = fail(BizCode.BAD_REQUEST, "场景名称已存在", f"当前工作空间下已存在名为「{request.scene_name}」的场景,请使用其他名称")
|
||||
return JSONResponse(status_code=400, content=msg)
|
||||
api_logger.error(f"Runtime error in scene creation: {err_str}", exc_info=True)
|
||||
return fail(BizCode.INTERNAL_ERROR, "场景创建失败", err_str)
|
||||
|
||||
except Exception as e:
|
||||
api_logger.error(f"Unexpected error in scene creation: {str(e)}", exc_info=True)
|
||||
@@ -661,7 +672,8 @@ async def get_scenes(
|
||||
async def create_class(
|
||||
request: ClassCreateRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
current_user: User = Depends(get_current_user),
|
||||
x_language_type: Optional[str] = Header(None, alias="X-Language-Type")
|
||||
):
|
||||
"""创建本体类型
|
||||
|
||||
@@ -676,7 +688,7 @@ async def create_class(
|
||||
ApiResponse: 包含创建的类型信息
|
||||
"""
|
||||
from app.controllers.ontology_secondary_routes import create_class_handler
|
||||
return await create_class_handler(request, db, current_user)
|
||||
return await create_class_handler(request, db, current_user, x_language_type)
|
||||
|
||||
|
||||
@router.put("/class/{class_id}", response_model=ApiResponse)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
from uuid import UUID
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi import Depends, Header
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.error_codes import BizCode
|
||||
@@ -231,7 +231,8 @@ async def scenes_handler(
|
||||
async def create_class_handler(
|
||||
request: ClassCreateRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
current_user: User = Depends(get_current_user),
|
||||
x_language_type: Optional[str] = None
|
||||
):
|
||||
"""创建本体类型(统一使用列表形式,支持单个或批量)"""
|
||||
|
||||
@@ -327,8 +328,20 @@ async def create_class_handler(
|
||||
return fail(BizCode.BAD_REQUEST, "请求参数无效", str(e))
|
||||
|
||||
except RuntimeError as e:
|
||||
api_logger.error(f"Runtime error in class creation: {str(e)}", exc_info=True)
|
||||
return fail(BizCode.INTERNAL_ERROR, "类型创建失败", str(e))
|
||||
err_str = str(e)
|
||||
if "UniqueViolation" in err_str or "uq_scene_class_name" in err_str:
|
||||
api_logger.warning(f"Duplicate class name in scene {request.scene_id}")
|
||||
from app.core.language_utils import get_language_from_header
|
||||
from fastapi.responses import JSONResponse
|
||||
lang = get_language_from_header(x_language_type)
|
||||
class_name = request.classes[0].class_name if request.classes else ""
|
||||
if lang == "en":
|
||||
msg = fail(BizCode.BAD_REQUEST, "Class name already exists", f"A class named \"{class_name}\" already exists in this scene. Please use a different name.")
|
||||
else:
|
||||
msg = fail(BizCode.BAD_REQUEST, "类型名称已存在", f"当前场景下已存在名为「{class_name}」的类型,请使用其他名称")
|
||||
return JSONResponse(status_code=400, content=msg)
|
||||
api_logger.error(f"Runtime error in class creation: {err_str}", exc_info=True)
|
||||
return fail(BizCode.INTERNAL_ERROR, "类型创建失败", err_str)
|
||||
|
||||
except Exception as e:
|
||||
api_logger.error(f"Unexpected error in class creation: {str(e)}", exc_info=True)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import datetime
|
||||
|
||||
from sqlalchemy import Boolean, Column, DateTime, Float, Integer, String
|
||||
from sqlalchemy import Boolean, Column, DateTime, Float, Integer, String, UniqueConstraint
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
|
||||
from app.db import Base
|
||||
@@ -9,6 +9,9 @@ from app.db import Base
|
||||
class MemoryConfig(Base):
|
||||
"""记忆配置表 - 用于存储记忆系统的配置参数"""
|
||||
__tablename__ = "memory_config"
|
||||
__table_args__ = (
|
||||
UniqueConstraint('workspace_id', 'config_name', name='uq_workspace_config_name'),
|
||||
)
|
||||
|
||||
# 主键
|
||||
config_id = Column(UUID(as_uuid=True), primary_key=True, comment="配置ID")
|
||||
|
||||
@@ -9,7 +9,7 @@ Classes:
|
||||
|
||||
import datetime
|
||||
import uuid
|
||||
from sqlalchemy import Column, String, DateTime, Text, ForeignKey, Boolean
|
||||
from sqlalchemy import Column, String, DateTime, Text, ForeignKey, Boolean, UniqueConstraint
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.db import Base
|
||||
@@ -18,6 +18,9 @@ from app.db import Base
|
||||
class OntologyClass(Base):
|
||||
"""本体类型表 - 用于存储某个场景提取出来的本体类型信息"""
|
||||
__tablename__ = "ontology_class"
|
||||
__table_args__ = (
|
||||
UniqueConstraint('scene_id', 'class_name', name='uq_scene_class_name'),
|
||||
)
|
||||
|
||||
# 主键
|
||||
class_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True, comment="类型ID")
|
||||
|
||||
@@ -107,6 +107,7 @@ def get_user_workspaces(db: Session, user: User) -> List[Workspace]:
|
||||
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
|
||||
@@ -1104,6 +1105,52 @@ def _fill_workspace_configs_model_defaults(
|
||||
)
|
||||
|
||||
|
||||
def _ensure_default_ontology_scenes(db: Session, workspace: Workspace) -> None:
|
||||
"""Ensure a workspace has default ontology scenes, creating them if missing.
|
||||
|
||||
Checks whether any is_system_default scene exists for the workspace.
|
||||
If not, runs the DefaultOntologyInitializer to create them.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
workspace: The workspace to check
|
||||
"""
|
||||
from app.models.ontology_scene import OntologyScene
|
||||
|
||||
# 幂等检查:是否已存在系统默认场景
|
||||
existing = db.query(OntologyScene).filter(
|
||||
OntologyScene.workspace_id == workspace.id,
|
||||
OntologyScene.is_system_default.is_(True)
|
||||
).first()
|
||||
|
||||
if existing:
|
||||
return
|
||||
|
||||
business_logger.info(
|
||||
f"Workspace {workspace.id} missing default ontology scenes, creating them"
|
||||
)
|
||||
|
||||
try:
|
||||
initializer = DefaultOntologyInitializer(db)
|
||||
success, error_msg = initializer.initialize_default_scenes(
|
||||
workspace.id, language="zh"
|
||||
)
|
||||
if success:
|
||||
db.commit()
|
||||
business_logger.info(
|
||||
f"为工作空间 {workspace.id} 补建默认本体场景成功"
|
||||
)
|
||||
else:
|
||||
business_logger.warning(
|
||||
f"为工作空间 {workspace.id} 补建默认本体场景失败: {error_msg}"
|
||||
)
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
business_logger.error(
|
||||
f"为工作空间 {workspace.id} 补建默认本体场景异常: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
def _create_default_memory_config(
|
||||
db: Session,
|
||||
workspace_id: uuid.UUID,
|
||||
|
||||
Reference in New Issue
Block a user