feat: Assistant api

This commit is contained in:
fit2cloud-chenyw
2025-07-09 18:29:07 +08:00
parent 32f0495f91
commit 4bd69b845f
9 changed files with 212 additions and 8 deletions
@@ -0,0 +1,35 @@
"""022_assistant_ddl
Revision ID: e6b20ae73606
Revises: 440e9e41da3c
Create Date: 2025-07-09 18:20:27.160183
"""
from alembic import op
import sqlalchemy as sa
import sqlmodel.sql.sqltypes
# revision identifiers, used by Alembic.
revision = 'e6b20ae73606'
down_revision = '440e9e41da3c'
branch_labels = None
depends_on = None
def upgrade():
op.create_table(
'sys_assistant',
sa.Column('id', sa.BigInteger(), primary_key=True, nullable=False),
sa.Column('name', sa.String(255), nullable=False),
sa.Column('domain', sa.String(255), nullable=False),
sa.Column('type', sa.Integer(), nullable=False, default=0),
sa.Column('configuration', sa.Text(), nullable=True),
sa.Column('create_time', sa.BigInteger(), default=0, nullable=False)
)
op.create_index(op.f('ix_sys_assistant_id'), 'sys_assistant', ['id'], unique=False)
def downgrade():
op.drop_index(op.f('ix_sys_assistant_id'), table_name='sys_assistant')
op.drop_table('sys_assistant')
+2 -1
View File
@@ -4,13 +4,14 @@ from apps.chat.api import chat
from apps.dashboard.api import dashboard_api
from apps.datasource.api import datasource
from apps.settings.api import terminology
from apps.system.api import login, user, aimodel, workspace
from apps.system.api import login, user, aimodel, workspace, assistant
from apps.mcp import mcp
api_router = APIRouter()
api_router.include_router(login.router)
api_router.include_router(user.router)
api_router.include_router(workspace.router)
api_router.include_router(assistant.router)
api_router.include_router(aimodel.router)
api_router.include_router(terminology.router)
api_router.include_router(datasource.router)
+93
View File
@@ -0,0 +1,93 @@
from datetime import timedelta
from fastapi import APIRouter, FastAPI, Request
from sqlmodel import Session, select
from apps.system.crud.user import get_user_by_account
from apps.system.models.system_model import AssistantModel
from apps.system.schemas.system_schema import AssistantBase, AssistantDTO, AssistantValidator
from common.core.deps import SessionDep
from common.core.security import create_access_token
from common.utils.time import get_timestamp
from starlette.middleware.cors import CORSMiddleware
from common.core.config import settings
router = APIRouter(tags=["system/assistant"], prefix="/system/assistant")
@router.get("/validator/{id}", response_model=AssistantValidator)
async def info(session: SessionDep, id: int):
db_model = session.get(AssistantModel, id)
if not db_model:
return AssistantValidator()
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
user = get_user_by_account(session=session, account='admin')
access_token = create_access_token(
user.to_dict(), expires_delta=access_token_expires
)
return AssistantValidator(True, True, True, access_token)
@router.get("", response_model=list[AssistantModel])
async def query(session: SessionDep):
list_result = session.exec(select(AssistantModel).order_by(AssistantModel.create_time)).all()
return list_result
@router.post("")
async def add(request: Request, session: SessionDep, creator: AssistantBase):
db_model = AssistantModel.model_validate(creator)
db_model.create_time = get_timestamp()
session.add(db_model)
session.commit()
dynamic_upgrade_cors(request=request, session=session)
@router.put("")
async def update(request: Request, session: SessionDep, editor: AssistantDTO):
id = editor.id
db_model = session.get(AssistantModel, id)
if not db_model:
raise ValueError(f"AssistantModel with id {id} not found")
update_data = AssistantModel.model_validate(editor)
db_model.sqlmodel_update(update_data)
session.add(db_model)
session.commit()
dynamic_upgrade_cors(request=request, session=session)
@router.get("/{id}", response_model=AssistantModel)
async def get_one(session: SessionDep, id: int):
db_model = session.get(AssistantModel, id)
if not db_model:
raise ValueError(f"AssistantModel with id {id} not found")
return db_model
@router.delete("/{id}")
async def delete(request: Request, session: SessionDep, id: int):
db_model = session.get(AssistantModel, id)
if not db_model:
raise ValueError(f"AssistantModel with id {id} not found")
session.delete(db_model)
session.commit()
dynamic_upgrade_cors(request=request, session=session)
def dynamic_upgrade_cors(request: Request, session: Session):
list_result = session.exec(select(AssistantModel).order_by(AssistantModel.create_time)).all()
seen = set()
unique_domains = []
for item in list_result:
if item.domain:
for domain in item.domain.split(','):
domain = domain.strip()
if domain and domain not in seen:
seen.add(domain)
unique_domains.append(domain)
app: FastAPI = request.app
cors_middleware = None
for middleware in app.user_middleware:
if middleware.cls == CORSMiddleware:
cors_middleware = middleware
break
if cors_middleware:
updated_origins = list(set(settings.all_cors_origins + unique_domains))
cors_middleware.kwargs['allow_origins'] = updated_origins
+14 -1
View File
@@ -1,4 +1,5 @@
from typing import Optional
from sqlmodel import BigInteger, Field, Text, SQLModel
from common.core.models import SnowflakeBase
from common.core.schemas import BaseCreatorDTO
@@ -39,4 +40,16 @@ class UserWsBaseModel(SQLModel):
weight: int = Field(default=0, nullable=False)
class UserWsModel(SnowflakeBase, UserWsBaseModel, table=True):
__tablename__ = "sys_user_ws"
__tablename__ = "sys_user_ws"
class AssistantBaseModel(SQLModel):
name: str = Field(max_length=255, nullable=False)
type: int = Field(nullable=False, default=0)
domain: str = Field(max_length=255, nullable=False)
configuration: Optional[str] = Field(sa_type = Text(), nullable=True)
create_time: int = Field(default=0, sa_type=BigInteger())
class AssistantModel(SnowflakeBase, AssistantBaseModel, table=True):
__tablename__ = "sys_assistant"
+33 -1
View File
@@ -1,3 +1,4 @@
from typing import Optional
from pydantic import BaseModel
from common.core.schemas import BaseCreatorDTO
@@ -51,4 +52,35 @@ class UserWsDTO(UserWsBase):
class UserInfoDTO(UserEditor):
language: str = "zh-CN"
weight: int = 0
isAdmin: bool = False
isAdmin: bool = False
class AssistantBase(BaseModel):
name: str
domain: str
type: int = 0
configuration: Optional[str] = None
class AssistantDTO(AssistantBase, BaseCreatorDTO):
pass
class AssistantValidator(BaseModel):
valid: bool = False
id_match: bool = False
domain_match: bool = False
token: Optional[str] = None
def __init__(
self,
valid: bool = False,
id_match: bool = False,
domain_match: bool = False,
token: Optional[str] = None,
**kwargs
):
super().__init__(
valid=valid,
id_match=id_match,
domain_match=domain_match,
token=token,
**kwargs
)
+2 -1
View File
@@ -25,7 +25,8 @@ wlist = [
"/system/license",
"/system/config/key",
"/images/*",
"/sse"
"/sse",
"/system/assistant/validator/*"
]
class WhitelistChecker: