"""FastAPI 大模型相关接口"""
import logging
from typing import cast
from fastapi import APIRouter, Depends, Request, status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse
from apps.dependency import verify_admin, verify_personal_token
from apps.schemas.request_data import (
UpdateLLMReq,
UpdateSpecialLlmReq,
)
from apps.schemas.response_data import (
ListLLMAdminRsp,
ListLLMRsp,
LLMAdminInfo,
LLMProviderInfo,
ResponseData,
)
from apps.services.llm import LLMManager
from apps.services.settings import SettingsManager
router = APIRouter(
prefix="/api/llm",
tags=["llm"],
dependencies=[
Depends(verify_personal_token),
],
)
admin_router = APIRouter(
prefix="/api/llm",
tags=["llm"],
dependencies=[
Depends(verify_personal_token),
Depends(verify_admin),
],
)
_logger = logging.getLogger(__name__)
@router.get("/provider", response_model=ListLLMRsp,
responses={status.HTTP_404_NOT_FOUND: {"model": ResponseData}},
)
async def list_llm(llmId: str | None = None) -> JSONResponse:
"""GET /llm: 获取大模型列表"""
llm_list_raw = await LLMManager.list_llm(llmId, admin_view=False)
if llm_list_raw and not all(isinstance(item, LLMProviderInfo) for item in llm_list_raw):
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content=jsonable_encoder(
ResponseData(
code=status.HTTP_500_INTERNAL_SERVER_ERROR,
message="大模型列表数据类型不符合预期",
result=None,
).model_dump(exclude_none=True, by_alias=True),
),
)
llm_list = cast("list[LLMProviderInfo]", llm_list_raw)
return JSONResponse(
status_code=status.HTTP_200_OK,
content=jsonable_encoder(
ListLLMRsp(
code=status.HTTP_200_OK,
message="success",
result=llm_list,
).model_dump(exclude_none=True, by_alias=True),
),
)
@admin_router.get("/config", response_model=ResponseData,
responses={status.HTTP_404_NOT_FOUND: {"model": ResponseData}},
)
async def get_llm_config(llmId: str) -> JSONResponse:
"""GET /llm/config: 获取单个大模型的详细配置信息(管理员视图)"""
llm = await LLMManager.get_llm(llmId)
if not llm:
return JSONResponse(
status_code=status.HTTP_404_NOT_FOUND,
content=jsonable_encoder(
ResponseData(
code=status.HTTP_404_NOT_FOUND,
message=f"大模型 {llmId} 不存在",
result=None,
).model_dump(exclude_none=True, by_alias=True),
),
)
llm_config = LLMAdminInfo(
llmId=llm.id,
llmDescription=llm.llmDescription,
llmType=llm.llmType,
baseUrl=llm.baseUrl,
apiKey=llm.apiKey,
modelName=llm.modelName,
maxTokens=llm.maxToken,
ctxLength=llm.ctxLength,
temperature=llm.temperature,
provider=llm.provider.value if llm.provider else None,
extraConfig=llm.extraConfig,
)
return JSONResponse(
status_code=status.HTTP_200_OK,
content=jsonable_encoder(
ListLLMAdminRsp(
code=status.HTTP_200_OK,
message="success",
result=llm_config,
).model_dump(exclude_none=True, by_alias=True),
),
)
@admin_router.put("/setting", response_model=ResponseData,
responses={status.HTTP_404_NOT_FOUND: {"model": ResponseData}},
)
async def change_global_llm(request: Request, req: UpdateSpecialLlmReq) -> JSONResponse:
"""PUT /llm/setting: 更改全局Function和Embedding模型设置(管理员)"""
try:
user_id = request.state.user_id
await SettingsManager.update_global_llm_settings(user_id, req)
return JSONResponse(
status_code=status.HTTP_200_OK,
content=jsonable_encoder(
ResponseData(
code=status.HTTP_200_OK,
message="success",
result={
"functionLLM": req.functionLLM,
"embeddingLLM": req.embeddingLLM,
},
).model_dump(exclude_none=True, by_alias=True),
),
)
except ValueError as e:
_logger.exception("[llm] 更改全局LLM设置失败")
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content=jsonable_encoder(
ResponseData(
code=status.HTTP_400_BAD_REQUEST,
message=str(e),
result=None,
).model_dump(exclude_none=True, by_alias=True),
),
)
@admin_router.put("",
responses={status.HTTP_404_NOT_FOUND: {"model": ResponseData}},
)
async def create_llm(req: UpdateLLMReq) -> JSONResponse:
"""PUT /llm: 创建或更新大模型配置"""
try:
await LLMManager.update_llm(req.llm_id, req)
except ValueError as e:
_logger.exception("[llm] 创建或更新大模型配置失败")
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content=jsonable_encoder(
ResponseData(
code=status.HTTP_500_INTERNAL_SERVER_ERROR,
message=str(e),
result=None,
).model_dump(exclude_none=True, by_alias=True),
),
)
return JSONResponse(
status_code=status.HTTP_200_OK,
content=jsonable_encoder(
ResponseData(
code=status.HTTP_200_OK,
message="success",
result=req.llm_id,
).model_dump(exclude_none=True, by_alias=True),
),
)
@admin_router.delete("",
responses={status.HTTP_404_NOT_FOUND: {"model": ResponseData}},
)
async def delete_llm(llmId: str) -> JSONResponse:
"""DELETE /llm: 删除大模型配置"""
try:
await LLMManager.delete_llm(llmId)
except ValueError as e:
_logger.exception("[llm] 删除大模型配置失败")
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content=jsonable_encoder(
ResponseData(
code=status.HTTP_500_INTERNAL_SERVER_ERROR,
message=str(e),
result=None,
).model_dump(exclude_none=True, by_alias=True),
),
)
return JSONResponse(
status_code=status.HTTP_200_OK,
content=jsonable_encoder(
ResponseData(
code=status.HTTP_200_OK,
message="success",
result=llmId,
).model_dump(exclude_none=True, by_alias=True),
),
)