diff --git a/backend/alembic/versions/018_modify_chat.py b/backend/alembic/versions/018_modify_chat.py new file mode 100644 index 00000000..07a3e8ff --- /dev/null +++ b/backend/alembic/versions/018_modify_chat.py @@ -0,0 +1,83 @@ +"""018_modify_chat + +Revision ID: 863105882eba +Revises: a0ba8268868d +Create Date: 2025-06-30 16:44:17.123791 + +""" +from alembic import op +import sqlalchemy as sa +import sqlmodel.sql.sqltypes +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '863105882eba' +down_revision = 'a0ba8268868d' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('chat', 'id', + existing_type=sa.INTEGER(), + type_=sa.BigInteger(), + existing_nullable=False, + autoincrement=True, + existing_server_default=sa.Identity(always=True, start=1, increment=1, minvalue=1, maxvalue=2147483647, cycle=False, cache=1)) + op.alter_column('chat', 'datasource', + existing_type=sa.INTEGER(), + type_=sa.BigInteger(), + existing_nullable=True) + op.alter_column('chat_record', 'id', + existing_type=sa.INTEGER(), + type_=sa.BigInteger(), + existing_nullable=False, + autoincrement=True, + existing_server_default=sa.Identity(always=True, start=1, increment=1, minvalue=1, maxvalue=2147483647, cycle=False, cache=1)) + op.alter_column('chat_record', 'chat_id', + existing_type=sa.INTEGER(), + type_=sa.BigInteger(), + existing_nullable=False) + op.alter_column('chat_record', 'ai_modal_id', + existing_type=sa.INTEGER(), + type_=sa.BigInteger(), + existing_nullable=True) + op.alter_column('chat_record', 'datasource', + existing_type=sa.INTEGER(), + type_=sa.BigInteger(), + existing_nullable=True) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('chat_record', 'datasource', + existing_type=sa.BigInteger(), + type_=sa.INTEGER(), + existing_nullable=True) + op.alter_column('chat_record', 'ai_modal_id', + existing_type=sa.BigInteger(), + type_=sa.INTEGER(), + existing_nullable=True) + op.alter_column('chat_record', 'chat_id', + existing_type=sa.BigInteger(), + type_=sa.INTEGER(), + existing_nullable=False) + op.alter_column('chat_record', 'id', + existing_type=sa.BigInteger(), + type_=sa.INTEGER(), + existing_nullable=False, + autoincrement=True, + existing_server_default=sa.Identity(always=True, start=1, increment=1, minvalue=1, maxvalue=2147483647, cycle=False, cache=1)) + op.alter_column('chat', 'datasource', + existing_type=sa.BigInteger(), + type_=sa.INTEGER(), + existing_nullable=True) + op.alter_column('chat', 'id', + existing_type=sa.BigInteger(), + type_=sa.INTEGER(), + existing_nullable=False, + autoincrement=True, + existing_server_default=sa.Identity(always=True, start=1, increment=1, minvalue=1, maxvalue=2147483647, cycle=False, cache=1)) + # ### end Alembic commands ### diff --git a/backend/apps/chat/curd/chat.py b/backend/apps/chat/curd/chat.py index 80b93b22..bf204645 100644 --- a/backend/apps/chat/curd/chat.py +++ b/backend/apps/chat/curd/chat.py @@ -61,7 +61,7 @@ def get_chat_with_records(session: SessionDep, chart_id: int, current_user: Curr ChatRecord.question, ChatRecord.sql_answer, ChatRecord.sql, ChatRecord.data, ChatRecord.chart_answer, ChatRecord.chart, ChatRecord.analysis, ChatRecord.predict, ChatRecord.datasource_select_answer, ChatRecord.recommended_question_answer, - ChatRecord.recommended_question, + ChatRecord.recommended_question, ChatRecord.first_chat, ChatRecord.predict_data, ChatRecord.finish, ChatRecord.error, ChatRecord.run_time)).filter( and_(Chat.create_by == current_user.id, ChatRecord.chat_id == chart_id)).order_by(ChatRecord.create_time).all() @@ -111,6 +111,29 @@ def create_chat(session: SessionDep, current_user: CurrentUser, create_chat_obj: chat_info.datasource_exists = True chat_info.datasource_name = ds.name + if require_datasource and ds: + # generate first empty record + record = ChatRecord() + record.chat_id = chat.id + record.datasource = ds.id + record.engine_type = ds.type_name + record.first_chat = True + record.finish = True + record.create_time = datetime.datetime.now() + record.create_by = current_user.id + + _record = ChatRecord(**record.model_dump()) + + session.add(record) + session.flush() + session.refresh(record) + _record.id = record.id + session.commit() + + # todo suggest questions + + chat_info.records.append(_record) + return chat_info @@ -131,6 +154,7 @@ def save_question(session: SessionDep, current_user: CurrentUser, question: Chat record.create_by = current_user.id record.datasource = chat.datasource record.engine_type = chat.engine_type + record.ai_modal_id = question.ai_modal_id result = ChatRecord(**record.model_dump()) diff --git a/backend/apps/chat/models/chat_model.py b/backend/apps/chat/models/chat_model.py index f5305b63..37325769 100644 --- a/backend/apps/chat/models/chat_model.py +++ b/backend/apps/chat/models/chat_model.py @@ -15,25 +15,25 @@ from apps.template.select_datasource.generator import get_datasource_template class Chat(SQLModel, table=True): __tablename__ = "chat" - id: Optional[int] = Field(sa_column=Column(Integer, Identity(always=True), primary_key=True)) + id: Optional[int] = Field(sa_column=Column(BigInteger, Identity(always=True), primary_key=True)) create_time: datetime = Field(sa_column=Column(DateTime(timezone=True), nullable=True)) create_by: int = Field(sa_column=Column(BigInteger, nullable=True)) brief: str = Field(max_length=64, nullable=True) chat_type: str = Field(max_length=20, default="chat") # chat, datasource - datasource: int = Field(sa_column=Column(Integer, nullable=True)) + datasource: int = Field(sa_column=Column(BigInteger, nullable=True)) engine_type: str = Field(max_length=64) class ChatRecord(SQLModel, table=True): __tablename__ = "chat_record" - id: Optional[int] = Field(sa_column=Column(Integer, Identity(always=True), primary_key=True)) - chat_id: int = Field(sa_column=Column(Integer, nullable=False)) - ai_modal_id: Optional[int] = Field(sa_column=Column(Integer)) + id: Optional[int] = Field(sa_column=Column(BigInteger, Identity(always=True), primary_key=True)) + chat_id: int = Field(sa_column=Column(BigInteger, nullable=False)) + ai_modal_id: Optional[int] = Field(sa_column=Column(BigInteger)) first_chat: bool = Field(sa_column=Column(Boolean, nullable=True, default=False)) create_time: datetime = Field(sa_column=Column(DateTime(timezone=True), nullable=True)) finish_time: datetime = Field(sa_column=Column(DateTime(timezone=True), nullable=True)) create_by: int = Field(sa_column=Column(BigInteger, nullable=True)) - datasource: int = Field(sa_column=Column(Integer, nullable=True)) + datasource: int = Field(sa_column=Column(BigInteger, nullable=True)) engine_type: str = Field(max_length=64) question: str = Field(sa_column=Column(Text, nullable=True)) sql_answer: str = Field(sa_column=Column(Text, nullable=True)) @@ -90,6 +90,7 @@ class ChatInfo(BaseModel): class AiModelQuestion(BaseModel): + ai_modal_id: int = None engine: str = "" db_schema: str = "" sql: str = "" diff --git a/backend/apps/chat/task/llm.py b/backend/apps/chat/task/llm.py index 5935f9f9..8cc9d80c 100644 --- a/backend/apps/chat/task/llm.py +++ b/backend/apps/chat/task/llm.py @@ -39,6 +39,7 @@ class LLMService: ds: CoreDatasource = None): self.ds = ds self.chat_question = chat_question + self.chat_question.ai_modal_id = aimodel.id self.config = get_llm_config(aimodel) # Create LLM instance through factory diff --git a/frontend/src/api/chat.ts b/frontend/src/api/chat.ts index 58a03ef4..3b9c7e31 100644 --- a/frontend/src/api/chat.ts +++ b/frontend/src/api/chat.ts @@ -22,7 +22,7 @@ export interface ChatMessage { content?: string | number record?: ChatRecord isTyping?: boolean - isWelcome?: boolean + first_chat?: boolean } export class ChatRecord { @@ -42,6 +42,7 @@ export class ChatRecord { finish?: boolean = false error?: string run_time: number = 0 + first_chat: boolean = false constructor() constructor( @@ -60,7 +61,8 @@ export class ChatRecord { predict_data: string | undefined, finish: boolean, error: string | undefined, - run_time: number + run_time: number, + first_chat: boolean ) constructor( id?: number, @@ -78,7 +80,8 @@ export class ChatRecord { predict_data?: string, finish?: boolean, error?: string, - run_time?: number + run_time?: number, + first_chat?: boolean ) { this.id = id this.chat_id = chat_id @@ -93,9 +96,10 @@ export class ChatRecord { this.analysis = analysis this.predict = predict this.predict_data = predict_data - this.finish = finish + this.finish = !!finish this.error = error this.run_time = run_time ?? 0 + this.first_chat = !!first_chat } } @@ -214,7 +218,8 @@ function toChatRecord(data?: any): ChatRecord | undefined { data.predict_data, data.finish, data.error, - data.run_time + data.run_time, + data.first_chat ) } diff --git a/frontend/src/utils/request.ts b/frontend/src/utils/request.ts index cf9e54b4..957cdc15 100644 --- a/frontend/src/utils/request.ts +++ b/frontend/src/utils/request.ts @@ -217,6 +217,10 @@ class HttpService { if (token) { heads['X-SQLBOT-TOKEN'] = `Bearer ${token}` } + // @ts-ignore + const request_key = LicenseGenerator.generate() + heads['X-SQLBOT-KEY'] = request_key + const real_url = import.meta.env.VITE_API_BASE_URL return fetch(real_url + url, { method: 'POST', diff --git a/frontend/src/views/chat/ChatRecordFirst.vue b/frontend/src/views/chat/ChatRecordFirst.vue new file mode 100644 index 00000000..8293a36d --- /dev/null +++ b/frontend/src/views/chat/ChatRecordFirst.vue @@ -0,0 +1,45 @@ + + + + + + + {{ t('qa.selected_datasource') }}: + + + {{ currentChat.datasource_name }} + + + + + + diff --git a/frontend/src/views/chat/ChatRow.vue b/frontend/src/views/chat/ChatRow.vue index 3c28e55f..cdfa8636 100644 --- a/frontend/src/views/chat/ChatRow.vue +++ b/frontend/src/views/chat/ChatRow.vue @@ -1,31 +1,16 @@ @@ -36,13 +21,13 @@ const _datasource = computed({ - + - + diff --git a/frontend/src/views/chat/WelcomeBlock.vue b/frontend/src/views/chat/WelcomeBlock.vue deleted file mode 100644 index c544b91b..00000000 --- a/frontend/src/views/chat/WelcomeBlock.vue +++ /dev/null @@ -1,182 +0,0 @@ - - - - - - {{ t('qa.greeting') }} - {{ t('qa.description') }} - - - - {{ t('qa.select_datasource') }} - {{ t('qa.view_more') }} - - - - - - - - - {{ t('qa.empty_datasource') }} - - - - - {{ t('qa.selected_datasource') }} - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/views/chat/index.vue b/frontend/src/views/chat/index.vue index 71ba0070..12ce52cd 100644 --- a/frontend/src/views/chat/index.vue +++ b/frontend/src/views/chat/index.vue @@ -39,11 +39,7 @@ - + @@ -172,21 +168,13 @@ const isAnalysisTyping = ref(false) const isPredictTyping = ref(false) const computedMessages = computed>(() => { - const firstMessage: ChatMessage = { - role: 'assistant', - create_time: currentChat.value?.create_time, - content: currentChat.value?.datasource, - isTyping: false, - isWelcome: true, - } const messages: Array = [] if (currentChatId.value === undefined) { return messages } - messages.push(firstMessage) for (let i = 0; i < currentChat.value.records.length; i++) { const record = currentChat.value.records[i] - if (record.question !== undefined) { + if (record.question !== undefined && !record.first_chat) { messages.push({ role: 'user', create_time: record.create_time, @@ -198,9 +186,12 @@ const computedMessages = computed>(() => { create_time: record.create_time, record: record, isTyping: i === currentChat.value.records.length - 1 && isTyping.value, + first_chat: record.first_chat, }) } + console.log(messages) + return messages }) @@ -283,7 +274,6 @@ onMounted(() => { const sendMessage = async () => { if (!inputMessage.value.trim()) return - if (computedMessages.value[0].content === undefined) return loading.value = true isTyping.value = true