Files
ai-agent-admin/backend-fastapi/ai_platform/api/app_api.py
T
2026-06-08 18:14:59 +08:00

296 lines
9.8 KiB
Python

"""
AI 应用 API
"""
import logging
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy import select, func
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.base_schema import PaginatedResponse, ResponseModel
from ai_platform.models import AIApp, LLMModel
from ai_platform.schemas.app_schema import (
AppCreate,
AppUpdate,
AppResponse,
AppListResponse,
)
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/apps", tags=["AI-应用"])
@router.get("", response_model=PaginatedResponse[AppListResponse], summary="应用列表")
async def list_apps(
name: Optional[str] = Query(None, description="名称"),
app_type: Optional[str] = Query(None, description="类型"),
status: Optional[str] = Query(None, description="状态"),
page: int = Query(1, ge=1, description="页码"),
page_size: int = Query(20, ge=1, le=100, alias="pageSize", description="每页数量"),
db: AsyncSession = Depends(get_db),
):
"""获取应用列表"""
query = select(AIApp).where(AIApp.is_deleted == False)
if name:
query = query.where(AIApp.name.ilike(f"%{name}%"))
if app_type:
query = query.where(AIApp.app_type == app_type)
if status:
query = query.where(AIApp.status == status)
# 获取总数
count_result = await db.execute(select(func.count()).select_from(query.subquery()))
total = count_result.scalar() or 0
# 分页
offset = (page - 1) * page_size
query = query.order_by(AIApp.sort.desc(), AIApp.sys_create_datetime.desc())
query = query.offset(offset).limit(page_size)
result = await db.execute(query)
apps = result.scalars().all()
items = []
for app in apps:
model_name = ""
if app.model_id:
model_result = await db.execute(
select(LLMModel).where(LLMModel.id == app.model_id)
)
model = model_result.scalar_one_or_none()
model_name = model.display_name if model else ""
items.append({
"id": app.id,
"name": app.name,
"code": app.code,
"description": app.description or "",
"icon": app.icon or "",
"app_type": app.app_type or "chat",
"status": app.status or "draft",
"model_name": model_name,
"is_public": app.is_public or False,
"conversation_count": app.conversation_count or 0,
"message_count": app.message_count or 0,
"sys_create_datetime": app.sys_create_datetime,
})
return PaginatedResponse(items=items, total=total)
@router.get("/published", response_model=List[AppListResponse], summary="获取已发布应用")
async def list_published_apps(db: AsyncSession = Depends(get_db)):
"""获取已发布的应用列表(用于用户选择)"""
query = select(AIApp).where(
AIApp.is_deleted == False,
AIApp.status == "published"
).order_by(AIApp.sort.desc())
result = await db.execute(query)
apps = result.scalars().all()
items = []
for app in apps:
model_name = ""
if app.model_id:
model_result = await db.execute(
select(LLMModel).where(LLMModel.id == app.model_id)
)
model = model_result.scalar_one_or_none()
model_name = model.display_name if model else ""
items.append({
"id": app.id,
"name": app.name,
"code": app.code,
"description": app.description or "",
"icon": app.icon or "",
"app_type": app.app_type or "chat",
"status": app.status or "draft",
"model_name": model_name,
"is_public": app.is_public or False,
"conversation_count": app.conversation_count or 0,
"message_count": app.message_count or 0,
"sys_create_datetime": app.sys_create_datetime,
})
return items
@router.get("/code/{code}", response_model=AppResponse, summary="根据编码获取应用")
async def get_app_by_code(code: str, db: AsyncSession = Depends(get_db)):
"""根据编码获取应用"""
result = await db.execute(
select(AIApp).where(AIApp.code == code, AIApp.is_deleted == False)
)
app = result.scalar_one_or_none()
if not app:
raise HTTPException(status_code=404, detail="应用不存在")
return await _build_app_response(app, db)
@router.get("/{app_id}", response_model=AppResponse, summary="应用详情")
async def get_app(app_id: str, db: AsyncSession = Depends(get_db)):
"""获取应用详情"""
result = await db.execute(
select(AIApp).where(AIApp.id == app_id, AIApp.is_deleted == False)
)
app = result.scalar_one_or_none()
if not app:
raise HTTPException(status_code=404, detail="应用不存在")
return await _build_app_response(app, db)
@router.post("", response_model=AppResponse, summary="创建应用")
async def create_app(data: AppCreate, db: AsyncSession = Depends(get_db)):
"""创建应用"""
# 检查编码是否重复
exists_result = await db.execute(
select(AIApp).where(AIApp.code == data.code, AIApp.is_deleted == False)
)
if exists_result.scalar_one_or_none():
raise HTTPException(status_code=400, detail=f"应用编码 {data.code} 已存在")
# 验证模型
if data.model_id:
model_result = await db.execute(
select(LLMModel).where(LLMModel.id == data.model_id, LLMModel.is_deleted == False)
)
if not model_result.scalar_one_or_none():
raise HTTPException(status_code=400, detail="模型不存在")
app = AIApp(**data.model_dump())
db.add(app)
await db.commit()
await db.refresh(app)
return await _build_app_response(app, db)
@router.put("/{app_id}", response_model=AppResponse, summary="更新应用")
async def update_app(
app_id: str,
data: AppUpdate,
db: AsyncSession = Depends(get_db),
):
"""更新应用"""
result = await db.execute(
select(AIApp).where(AIApp.id == app_id, AIApp.is_deleted == False)
)
app = result.scalar_one_or_none()
if not app:
raise HTTPException(status_code=404, detail="应用不存在")
update_data = data.model_dump(exclude_unset=True)
# 验证模型
if "model_id" in update_data and update_data["model_id"]:
model_result = await db.execute(
select(LLMModel).where(
LLMModel.id == update_data["model_id"],
LLMModel.is_deleted == False
)
)
if not model_result.scalar_one_or_none():
raise HTTPException(status_code=400, detail="模型不存在")
for key, value in update_data.items():
setattr(app, key, value)
await db.commit()
await db.refresh(app)
return await _build_app_response(app, db)
@router.delete("/{app_id}", response_model=ResponseModel, summary="删除应用")
async def delete_app(app_id: str, db: AsyncSession = Depends(get_db)):
"""删除应用"""
result = await db.execute(
select(AIApp).where(AIApp.id == app_id, AIApp.is_deleted == False)
)
app = result.scalar_one_or_none()
if not app:
raise HTTPException(status_code=404, detail="应用不存在")
app.is_deleted = True
await db.commit()
return ResponseModel(message="删除成功")
@router.post("/{app_id}/publish", response_model=AppResponse, summary="发布应用")
async def publish_app(app_id: str, db: AsyncSession = Depends(get_db)):
"""发布应用"""
result = await db.execute(
select(AIApp).where(AIApp.id == app_id, AIApp.is_deleted == False)
)
app = result.scalar_one_or_none()
if not app:
raise HTTPException(status_code=404, detail="应用不存在")
app.status = "published"
await db.commit()
await db.refresh(app)
return await _build_app_response(app, db)
@router.post("/{app_id}/disable", response_model=AppResponse, summary="停用应用")
async def disable_app(app_id: str, db: AsyncSession = Depends(get_db)):
"""停用应用"""
result = await db.execute(
select(AIApp).where(AIApp.id == app_id, AIApp.is_deleted == False)
)
app = result.scalar_one_or_none()
if not app:
raise HTTPException(status_code=404, detail="应用不存在")
app.status = "disabled"
await db.commit()
await db.refresh(app)
return await _build_app_response(app, db)
async def _build_app_response(app: AIApp, db: AsyncSession) -> dict:
"""构建应用输出"""
model_name = ""
if app.model_id:
model_result = await db.execute(
select(LLMModel).where(LLMModel.id == app.model_id)
)
model = model_result.scalar_one_or_none()
model_name = model.display_name if model else ""
return {
"id": app.id,
"name": app.name,
"code": app.code,
"description": app.description or "",
"icon": app.icon or "",
"app_type": app.app_type or "chat",
"status": app.status or "draft",
"model_id": app.model_id,
"model_name": model_name,
"system_prompt": app.system_prompt or "",
"temperature": app.temperature or 0.7,
"top_p": app.top_p or 1.0,
"max_tokens": app.max_tokens or 2048,
"opening_statement": app.opening_statement or "",
"suggested_questions": app.suggested_questions or [],
"workflow_definition": app.workflow_definition or {},
"is_public": app.is_public or False,
"conversation_count": app.conversation_count or 0,
"message_count": app.message_count or 0,
"sort": app.sort or 0,
"sys_create_datetime": app.sys_create_datetime,
"sys_update_datetime": app.sys_update_datetime,
}