mirror of
https://gh-proxy.org/https://github.com/klinsmaya/SQLBot.git
synced 2026-11-08 01:06:59 +08:00
feat: Assistant api
This commit is contained in:
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -25,7 +25,8 @@ wlist = [
|
||||
"/system/license",
|
||||
"/system/config/key",
|
||||
"/images/*",
|
||||
"/sse"
|
||||
"/sse",
|
||||
"/system/assistant/validator/*"
|
||||
]
|
||||
|
||||
class WhitelistChecker:
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { request } from '@/utils/request'
|
||||
|
||||
export const assistantApi = {
|
||||
queryAll: (keyword?: string) =>
|
||||
request.get('/system/assistant', { params: keyword ? { keyword } : {} }),
|
||||
add: (data: any) => request.post('/system/assistant', data),
|
||||
edit: (data: any) => request.put('/system/assistant', data),
|
||||
delete: (id: number) => request.delete(`/system/assistant/${id}`),
|
||||
query: (id: number) => request.get(`/system/assistant/${id}`),
|
||||
validate: (id: any) => request.get(`/system/assistant/validator/${id}`),
|
||||
}
|
||||
@@ -6,12 +6,12 @@ const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
path: '/:id',
|
||||
name: 'index',
|
||||
component: assistant,
|
||||
},
|
||||
{
|
||||
path: '/assistant',
|
||||
path: '/assistant:id',
|
||||
name: 'assistant',
|
||||
component: assistant,
|
||||
},
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<h4>SQLBot 小助手</h4>
|
||||
</div>
|
||||
</div>
|
||||
<chat-component ref="chatRef" class="sqlbot-chat-container" />
|
||||
<chat-component v-if="!loading" ref="chatRef" class="sqlbot-chat-container" />
|
||||
</div>
|
||||
<div class="sqlbot-top-btn">
|
||||
<el-icon style="cursor: pointer" @click="openHistory">
|
||||
@@ -54,12 +54,17 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { onBeforeMount, ref } from 'vue'
|
||||
import ChatComponent from '@/views/chat/index.vue'
|
||||
import AssistantGif from '@/assets/img/assistant.gif'
|
||||
import history from '@/assets/svg/chart/history.svg'
|
||||
import IconOpeEdit from '@/assets/svg/operate/ope-edit.svg'
|
||||
import IconOpeDelete from '@/assets/svg/operate/ope-delete.svg'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { assistantApi } from '@/api/assistant'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
const userStore = useUserStore()
|
||||
const route = useRoute()
|
||||
|
||||
const chatRef = ref()
|
||||
const chatList = ref<Array<any>>([])
|
||||
@@ -81,6 +86,19 @@ const EditPen = (chat: any) => {
|
||||
const Delete = (chat: any) => {
|
||||
chatRef.value?.onChatDeleted(chat.id)
|
||||
}
|
||||
const validator = ref({
|
||||
id: '',
|
||||
valid: false,
|
||||
id_match: false,
|
||||
token: '',
|
||||
})
|
||||
const loading = ref(true)
|
||||
onBeforeMount(async () => {
|
||||
const assistantId = route.params.id
|
||||
validator.value = await assistantApi.validate(assistantId)
|
||||
userStore.setToken(validator.value.token)
|
||||
loading.value = false
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
Reference in New Issue
Block a user