Skip to content

Commit 1e11cce

Browse files
committed
Merge remote-tracking branch 'origin/main'
# Conflicts: # frontend/src/router/index.ts
2 parents 22973e9 + 93798b7 commit 1e11cce

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+2176
-249
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""028_ds_oid
2+
3+
Revision ID: e96b16d3daab
4+
Revises: b049c9f8ca5b
5+
Create Date: 2025-07-17 14:40:48.522033
6+
7+
"""
8+
import sqlalchemy as sa
9+
from alembic import op
10+
11+
# revision identifiers, used by Alembic.
12+
revision = 'e96b16d3daab'
13+
down_revision = 'b049c9f8ca5b'
14+
branch_labels = None
15+
depends_on = None
16+
17+
18+
def upgrade():
19+
# ### commands auto generated by Alembic - please adjust! ###
20+
op.add_column('core_datasource', sa.Column('oid', sa.BigInteger(), nullable=True))
21+
op.execute('update core_datasource set oid = 1')
22+
# ### end Alembic commands ###
23+
24+
25+
def downgrade():
26+
# ### commands auto generated by Alembic - please adjust! ###
27+
op.drop_column('core_datasource', 'oid')
28+
# ### end Alembic commands ###
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""029_modify_chat
2+
3+
Revision ID: 77d4c39ec22f
4+
Revises: e96b16d3daab
5+
Create Date: 2025-07-17 17:05:13.392973
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
import sqlmodel.sql.sqltypes
11+
from sqlalchemy.dialects import postgresql
12+
13+
# revision identifiers, used by Alembic.
14+
revision = '77d4c39ec22f'
15+
down_revision = 'e96b16d3daab'
16+
branch_labels = None
17+
depends_on = None
18+
19+
20+
def upgrade():
21+
# ### commands auto generated by Alembic - please adjust! ###
22+
op.add_column('chat', sa.Column('oid', sa.BigInteger(), nullable=True))
23+
op.execute('update chat set oid = 1')
24+
op.alter_column('chat', 'create_time',
25+
existing_type=postgresql.TIMESTAMP(timezone=True),
26+
type_=sa.DateTime(),
27+
existing_nullable=True)
28+
op.alter_column('chat_record', 'create_time',
29+
existing_type=postgresql.TIMESTAMP(timezone=True),
30+
type_=sa.DateTime(),
31+
existing_nullable=True)
32+
op.alter_column('chat_record', 'finish_time',
33+
existing_type=postgresql.TIMESTAMP(timezone=True),
34+
type_=sa.DateTime(),
35+
existing_nullable=True)
36+
# ### end Alembic commands ###
37+
38+
39+
def downgrade():
40+
# ### commands auto generated by Alembic - please adjust! ###
41+
op.alter_column('chat_record', 'finish_time',
42+
existing_type=sa.DateTime(),
43+
type_=postgresql.TIMESTAMP(timezone=True),
44+
existing_nullable=True)
45+
op.alter_column('chat_record', 'create_time',
46+
existing_type=sa.DateTime(),
47+
type_=postgresql.TIMESTAMP(timezone=True),
48+
existing_nullable=True)
49+
op.alter_column('chat', 'create_time',
50+
existing_type=sa.DateTime(),
51+
type_=postgresql.TIMESTAMP(timezone=True),
52+
existing_nullable=True)
53+
op.drop_column('chat', 'oid')
54+
# ### end Alembic commands ###

backend/apps/chat/curd/chat.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import orjson
55
from sqlalchemy import and_
66
from sqlalchemy.orm import load_only
7-
from sqlmodel import select
87

98
from apps.chat.models.chat_model import Chat, ChatRecord, CreateChat, ChatInfo, RenameChat, ChatQuestion
109
from apps.datasource.models.datasource import CoreDatasource
@@ -13,8 +12,9 @@
1312

1413

1514
def list_chats(session: SessionDep, current_user: CurrentUser) -> List[Chat]:
16-
chart_list = session.exec(
17-
select(Chat).where(Chat.create_by == current_user.id).order_by(Chat.create_time.desc())).all()
15+
oid = current_user.oid if current_user.oid is not None else 1
16+
chart_list = session.query(Chat).filter(and_(Chat.create_by == current_user.id, Chat.oid == oid)).order_by(
17+
Chat.create_time.desc()).all()
1818
return chart_list
1919

2020

@@ -149,6 +149,7 @@ def create_chat(session: SessionDep, current_user: CurrentUser, create_chat_obj:
149149

150150
chat = Chat(create_time=datetime.datetime.now(),
151151
create_by=current_user.id,
152+
oid=current_user.oid if current_user.oid is not None else 1,
152153
brief=create_chat_obj.question.strip()[:20])
153154
ds: CoreDatasource | None = None
154155
if create_chat_obj.datasource:

backend/apps/chat/models/chat_model.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
class Chat(SQLModel, table=True):
1818
__tablename__ = "chat"
1919
id: Optional[int] = Field(sa_column=Column(BigInteger, Identity(always=True), primary_key=True))
20-
create_time: datetime = Field(sa_column=Column(DateTime(timezone=True), nullable=True))
20+
oid: Optional[int] = Field(sa_column=Column(BigInteger, nullable=True, default=1))
21+
create_time: datetime = Field(sa_column=Column(DateTime(timezone=False), nullable=True))
2122
create_by: int = Field(sa_column=Column(BigInteger, nullable=True))
2223
brief: str = Field(max_length=64, nullable=True)
2324
chat_type: str = Field(max_length=20, default="chat") # chat, datasource
@@ -31,8 +32,8 @@ class ChatRecord(SQLModel, table=True):
3132
chat_id: int = Field(sa_column=Column(BigInteger, nullable=False))
3233
ai_modal_id: Optional[int] = Field(sa_column=Column(BigInteger))
3334
first_chat: bool = Field(sa_column=Column(Boolean, nullable=True, default=False))
34-
create_time: datetime = Field(sa_column=Column(DateTime(timezone=True), nullable=True))
35-
finish_time: datetime = Field(sa_column=Column(DateTime(timezone=True), nullable=True))
35+
create_time: datetime = Field(sa_column=Column(DateTime(timezone=False), nullable=True))
36+
finish_time: datetime = Field(sa_column=Column(DateTime(timezone=False), nullable=True))
3637
create_by: int = Field(sa_column=Column(BigInteger, nullable=True))
3738
datasource: int = Field(sa_column=Column(BigInteger, nullable=True))
3839
engine_type: str = Field(max_length=64)
@@ -66,6 +67,7 @@ class ChatRecord(SQLModel, table=True):
6667
analysis_record_id: int = Field(sa_column=Column(BigInteger, nullable=True))
6768
predict_record_id: int = Field(sa_column=Column(BigInteger, nullable=True))
6869

70+
6971
class CreateChat(BaseModel):
7072
id: int = None
7173
question: str = None

backend/apps/chat/task/llm.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,8 @@ def run_analysis_or_predict_task(llm_service: LLMService, action_type: str, base
846846
try:
847847
llm_service.set_record(save_analysis_predict_record(llm_service.session, base_record, action_type))
848848

849+
yield orjson.dumps({'type': 'id', 'id': llm_service.get_record().id}).decode() + '\n\n'
850+
849851
if action_type == 'analysis':
850852
# generate analysis
851853
analysis_res = llm_service.generate_analysis()

backend/apps/datasource/api/datasource.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919

2020

2121
@router.get("/list")
22-
async def datasource_list(session: SessionDep):
23-
return get_datasource_list(session=session)
22+
async def datasource_list(session: SessionDep, user: CurrentUser):
23+
return get_datasource_list(session=session, user=user)
2424

2525

2626
@router.post("/get/{id}")
@@ -44,8 +44,8 @@ async def choose_tables(session: SessionDep, id: int, tables: List[CoreTable]):
4444

4545

4646
@router.post("/update", response_model=CoreDatasource)
47-
async def update(session: SessionDep, ds: CoreDatasource):
48-
return update_ds(session, ds)
47+
async def update(session: SessionDep,user: CurrentUser, ds: CoreDatasource):
48+
return update_ds(session,user, ds)
4949

5050

5151
@router.post("/delete/{id}", response_model=CoreDatasource)

backend/apps/datasource/crud/datasource.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@
2222
DatasourceConf, TableAndFields
2323

2424

25-
def get_datasource_list(session: SessionDep):
26-
statement = select(CoreDatasource).order_by(CoreDatasource.create_time.desc())
27-
datasource_list = session.exec(statement).fetchall()
28-
return datasource_list
25+
def get_datasource_list(session: SessionDep, user: CurrentUser):
26+
oid = user.oid if user.oid is not None else 1
27+
return session.query(CoreDatasource).filter(CoreDatasource.oid == oid).order_by(
28+
CoreDatasource.create_time.desc()).all()
2929

3030

3131
def get_ds(session: SessionDep, id: int):
@@ -45,25 +45,27 @@ def check_status(session: SessionDep, ds: CoreDatasource):
4545
return False
4646

4747

48-
def check_name(session: SessionDep, ds: CoreDatasource):
48+
def check_name(session: SessionDep, user: CurrentUser, ds: CoreDatasource):
4949
if ds.id is not None:
5050
ds_list = session.query(CoreDatasource).filter(
51-
and_(CoreDatasource.name == ds.name, CoreDatasource.id != ds.id)).all()
51+
and_(CoreDatasource.name == ds.name, CoreDatasource.id != ds.id, CoreDatasource.oid == user.oid)).all()
5252
if ds_list is not None and len(ds_list) > 0:
5353
raise 'Name exist'
5454
else:
55-
ds_list = session.query(CoreDatasource).filter(CoreDatasource.name == ds.name).all()
55+
ds_list = session.query(CoreDatasource).filter(
56+
and_(CoreDatasource.name == ds.name, CoreDatasource.oid == user.oid)).all()
5657
if ds_list is not None and len(ds_list) > 0:
5758
raise 'Name exist'
5859

5960

6061
def create_ds(session: SessionDep, user: CurrentUser, create_ds: CreateDatasource):
6162
ds = CoreDatasource()
6263
deepcopy_ignore_extra(create_ds, ds)
63-
check_name(session, ds)
64+
check_name(session, user, ds)
6465
ds.create_time = datetime.datetime.now()
6566
# status = check_status(session, ds)
6667
ds.create_by = user.id
68+
ds.oid = user.oid if user.oid is not None else 1
6769
ds.status = "Success"
6870
ds.type_name = db_type_relation()[ds.type]
6971
record = CoreDatasource(**ds.model_dump())
@@ -85,9 +87,9 @@ def chooseTables(session: SessionDep, id: int, tables: List[CoreTable]):
8587
updateNum(session, ds)
8688

8789

88-
def update_ds(session: SessionDep, ds: CoreDatasource):
90+
def update_ds(session: SessionDep, user: CurrentUser, ds: CoreDatasource):
8991
ds.id = int(ds.id)
90-
check_name(session, ds)
92+
check_name(session, user, ds)
9193
status = check_status(session, ds)
9294
ds.status = "Success" if status is True else "Fail"
9395
record = session.exec(select(CoreDatasource).where(CoreDatasource.id == ds.id)).first()

backend/apps/datasource/models/datasource.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class CoreDatasource(SQLModel, table=True):
1818
create_by: int = Field(sa_column=Column(BigInteger()))
1919
status: str = Field(max_length=64, nullable=True)
2020
num: str = Field(max_length=256, nullable=True)
21+
oid: int = Field(sa_column=Column(BigInteger()))
2122

2223

2324
class CoreTable(SQLModel, table=True):
@@ -53,6 +54,7 @@ class CreateDatasource(BaseModel):
5354
create_by: int = 0
5455
status: str = ''
5556
num: str = ''
57+
oid: int = 1
5658
tables: List[CoreTable] = []
5759

5860

backend/apps/mcp/mcp.py

Lines changed: 60 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,33 @@
33

44
from datetime import timedelta
55

6-
from fastapi import APIRouter, HTTPException
6+
import jwt
7+
from fastapi import HTTPException, status, APIRouter
78
from fastapi.responses import StreamingResponse
9+
# from fastapi.security import OAuth2PasswordBearer
10+
from jwt.exceptions import InvalidTokenError
11+
from pydantic import ValidationError
12+
from sqlmodel import select
813

914
from apps.chat.api.chat import create_chat
1015
from apps.chat.models.chat_model import ChatMcp, CreateChat, ChatStart
1116
from apps.chat.task.llm import LLMService, run_task
12-
from apps.datasource.crud.datasource import get_datasource_list
1317
from apps.system.crud.user import authenticate
14-
from apps.system.models.system_model import AiModelDetail
18+
from apps.system.crud.user import get_db_user
19+
from apps.system.models.system_model import UserWsModel
20+
from apps.system.models.user import UserModel
21+
from apps.system.schemas.system_schema import BaseUserDTO
22+
from apps.system.schemas.system_schema import UserInfoDTO
23+
from common.core import security
1524
from common.core.config import settings
16-
from common.core.deps import SessionDep, get_current_user
17-
from common.core.schemas import Token
25+
from common.core.deps import SessionDep
26+
from common.core.schemas import TokenPayload, XOAuth2PasswordBearer, Token
1827
from common.core.security import create_access_token
1928

29+
reusable_oauth2 = XOAuth2PasswordBearer(
30+
tokenUrl=f"{settings.API_V1_STR}/login/access-token"
31+
)
32+
2033
router = APIRouter(tags=["mcp"], prefix="/mcp")
2134

2235

@@ -35,21 +48,24 @@
3548
# ))
3649

3750

38-
@router.get("/ds_list", operation_id="get_datasource_list")
39-
async def datasource_list(session: SessionDep):
40-
return get_datasource_list(session=session)
41-
42-
43-
@router.get("/model_list", operation_id="get_model_list")
44-
async def get_model_list(session: SessionDep):
45-
return session.query(AiModelDetail).all()
51+
# @router.get("/ds_list", operation_id="get_datasource_list")
52+
# async def datasource_list(session: SessionDep):
53+
# return get_datasource_list(session=session)
54+
#
55+
#
56+
# @router.get("/model_list", operation_id="get_model_list")
57+
# async def get_model_list(session: SessionDep):
58+
# return session.query(AiModelDetail).all()
4659

4760

4861
@router.post("/mcp_start", operation_id="mcp_start")
4962
async def mcp_start(session: SessionDep, chat: ChatStart):
50-
user = authenticate(session=session, account=chat.username, password=chat.password)
63+
user: BaseUserDTO = authenticate(session=session, account=chat.username, password=chat.password)
5164
if not user:
5265
raise HTTPException(status_code=400, detail="Incorrect account or password")
66+
67+
if not user.oid or user.oid == 0:
68+
raise HTTPException(status_code=400, detail="No associated workspace, Please contact the administrator")
5369
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
5470
user_dict = user.to_dict()
5571
t = Token(access_token=create_access_token(
@@ -61,9 +77,36 @@ async def mcp_start(session: SessionDep, chat: ChatStart):
6177

6278
@router.post("/mcp_question", operation_id="mcp_question")
6379
async def mcp_question(session: SessionDep, chat: ChatMcp):
64-
user = await get_current_user(session, chat.token)
65-
66-
llm_service = LLMService(session, user, chat)
80+
try:
81+
payload = jwt.decode(
82+
chat.token, settings.SECRET_KEY, algorithms=[security.ALGORITHM]
83+
)
84+
token_data = TokenPayload(**payload)
85+
except (InvalidTokenError, ValidationError):
86+
raise HTTPException(
87+
status_code=status.HTTP_403_FORBIDDEN,
88+
detail="Could not validate credentials",
89+
)
90+
# session_user = await get_user_info(session=session, user_id=token_data.id)
91+
92+
db_user: UserModel = get_db_user(session=session, user_id=token_data.id)
93+
session_user = UserInfoDTO.model_validate(db_user.model_dump())
94+
session_user.isAdmin = session_user.id == 1 and session_user.account == 'admin'
95+
if session_user.isAdmin:
96+
session_user = session_user
97+
ws_model: UserWsModel = session.exec(
98+
select(UserWsModel).where(UserWsModel.uid == session_user.id, UserWsModel.oid == session_user.oid)).first()
99+
session_user.weight = ws_model.weight if ws_model else -1
100+
101+
session_user = UserInfoDTO.model_validate(session_user)
102+
if not session_user:
103+
raise HTTPException(status_code=404, detail="User not found")
104+
105+
if session_user.status != 1:
106+
raise HTTPException(status_code=400, detail="Inactive user")
107+
108+
# ask
109+
llm_service = LLMService(session, session_user, chat)
67110
llm_service.init_record()
68111

69112
return StreamingResponse(run_task(llm_service, False), media_type="text/event-stream")

0 commit comments

Comments
 (0)