From 0ecca1e21f9cb313db702f7785006e8edf220574 Mon Sep 17 00:00:00 2001 From: ulleo Date: Fri, 23 May 2025 18:26:15 +0800 Subject: [PATCH 1/9] feat: Rename & Delete Chat --- .../versions/ff653d5df198_007_add_chat.py | 52 +++++ backend/apps/chat/api/chat.py | 27 ++- backend/apps/chat/curd/chat.py | 30 ++- backend/apps/chat/models/chat_model.py | 5 + frontend/components.d.ts | 56 ----- frontend/src/api/chat.ts | 12 +- frontend/src/components/layout/index.vue | 140 +++++++++---- frontend/src/views/chat/ChatList.vue | 191 ++++++++++++++++++ frontend/src/views/chat/index.vue | 68 +++++-- 9 files changed, 455 insertions(+), 126 deletions(-) create mode 100644 backend/alembic/versions/ff653d5df198_007_add_chat.py delete mode 100644 frontend/components.d.ts create mode 100644 frontend/src/views/chat/ChatList.vue diff --git a/backend/alembic/versions/ff653d5df198_007_add_chat.py b/backend/alembic/versions/ff653d5df198_007_add_chat.py new file mode 100644 index 00000000..c5be2baf --- /dev/null +++ b/backend/alembic/versions/ff653d5df198_007_add_chat.py @@ -0,0 +1,52 @@ +"""006_add_chat + +Revision ID: ff653d5df198 +Revises: 0a6f11be9be4 +Create Date: 2025-05-21 15:44:29.763091 + +""" +from alembic import op +import sqlalchemy as sa +import sqlmodel.sql.sqltypes +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = 'ff653d5df198' +down_revision = 'e6276ddab06e' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('chat', + sa.Column('id', sa.Integer(), sa.Identity(always=True), nullable=False), + sa.Column('create_time', sa.DateTime(timezone=True), nullable=True), + sa.Column('create_by', sa.BigInteger(), nullable=True), + sa.Column('brief', sqlmodel.sql.sqltypes.AutoString(length=64), nullable=True), + sa.Column('chat_type', sqlmodel.sql.sqltypes.AutoString(length=20), nullable=False), + sa.Column('datasource', sa.Integer(), nullable=False), + sa.Column('engine_type', sqlmodel.sql.sqltypes.AutoString(length=64), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('chat_record', + sa.Column('id', sa.Integer(), sa.Identity(always=True), nullable=False), + sa.Column('chat_id', sa.Integer(), nullable=True), + sa.Column('create_time', sa.DateTime(timezone=True), nullable=True), + sa.Column('create_by', sa.BigInteger(), nullable=True), + sa.Column('datasource', sa.Integer(), nullable=False), + sa.Column('engine_type', sqlmodel.sql.sqltypes.AutoString(length=64), nullable=False), + sa.Column('question', sa.Text(), nullable=True), + sa.Column('full_question', sa.Text(), nullable=True), + sa.Column('answer', sa.Text(), nullable=True), + sa.Column('run_time', sa.Float(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('chat_record') + op.drop_table('chat') + # ### end Alembic commands ### diff --git a/backend/apps/chat/api/chat.py b/backend/apps/chat/api/chat.py index 4da08b13..02a76c06 100644 --- a/backend/apps/chat/api/chat.py +++ b/backend/apps/chat/api/chat.py @@ -2,8 +2,9 @@ from fastapi import APIRouter, HTTPException from fastapi.responses import StreamingResponse from sqlmodel import select -from apps.chat.curd.chat import list_chats, get_chat_with_records, create_chat, save_question, save_answer -from apps.chat.models.chat_model import CreateChat, ChatRecord +from apps.chat.curd.chat import list_chats, get_chat_with_records, create_chat, save_question, save_answer, rename_chat, \ + delete_chat +from apps.chat.models.chat_model import CreateChat, ChatRecord, RenameChat from apps.chat.schemas.chat_base_schema import LLMConfig from apps.chat.schemas.chat_schema import ChatQuestion from apps.chat.schemas.llm import AgentService @@ -32,6 +33,28 @@ async def list_chat(session: SessionDep, current_user: CurrentUser, chart_id: in ) +@router.post("/rename") +async def rename(session: SessionDep, chat: RenameChat): + try: + return rename_chat(session=session, rename_object=chat) + except Exception as e: + raise HTTPException( + status_code=500, + detail=str(e) + ) + + +@router.get("/delete/{chart_id}") +async def delete(session: SessionDep, chart_id: int): + try: + return delete_chat(session=session, chart_id=chart_id) + except Exception as e: + raise HTTPException( + status_code=500, + detail=str(e) + ) + + @router.post("/start") async def start_chat(session: SessionDep, current_user: CurrentUser, create_chat_obj: CreateChat): try: diff --git a/backend/apps/chat/curd/chat.py b/backend/apps/chat/curd/chat.py index a59e185d..930c4ff3 100644 --- a/backend/apps/chat/curd/chat.py +++ b/backend/apps/chat/curd/chat.py @@ -5,7 +5,7 @@ from typing import List from sqlalchemy import text, and_ from sqlmodel import select -from apps.chat.models.chat_model import Chat, ChatRecord, CreateChat, ChatInfo +from apps.chat.models.chat_model import Chat, ChatRecord, CreateChat, ChatInfo, RenameChat from apps.chat.schemas.chat_schema import ChatQuestion from apps.datasource.models.datasource import CoreDatasource from common.core.deps import SessionDep, CurrentUser @@ -17,6 +17,32 @@ def list_chats(session: SessionDep, current_user: CurrentUser) -> List[Chat]: return chart_list +def rename_chat(session: SessionDep, rename_object: RenameChat) -> str: + chat = session.query(Chat).filter(Chat.id == rename_object.id).first() + if not chat: + raise Exception(f"Chat with id {rename_object.id} not found") + + chat.brief = rename_object.brief.strip()[:20] + session.add(chat) + session.flush() + session.refresh(chat) + + brief = chat.brief + session.commit() + return brief + + +def delete_chat(session, chart_id) -> str: + chat = session.query(Chat).filter(Chat.id == chart_id).first() + if not chat: + return f'Chat with id {chart_id} has been deleted' + + session.delete(chat) + session.commit() + + return f'Chat with id {chart_id} has been deleted' + + def get_chat_with_records(session: SessionDep, chart_id: int, current_user: CurrentUser) -> ChatInfo: chat = session.query(Chat).filter(Chat.id == chart_id).first() if not chat: @@ -98,6 +124,7 @@ def save_question(session: SessionDep, current_user: CurrentUser, question: Chat return result + def save_full_question(session: SessionDep, id: int, full_question: str) -> ChatRecord: if not id: raise Exception("Record id cannot be None") @@ -114,6 +141,7 @@ def save_full_question(session: SessionDep, id: int, full_question: str) -> Chat return result + def save_answer(session: SessionDep, id: int, answer: str) -> ChatRecord: if not id: raise Exception("Record id cannot be None") diff --git a/backend/apps/chat/models/chat_model.py b/backend/apps/chat/models/chat_model.py index c747ae52..1c276caa 100644 --- a/backend/apps/chat/models/chat_model.py +++ b/backend/apps/chat/models/chat_model.py @@ -36,6 +36,11 @@ class CreateChat(BaseModel): datasource: int = None +class RenameChat(BaseModel): + id: int = None + brief: str = '' + + class ChatInfo(BaseModel): id: Optional[int] = None create_time: datetime = None diff --git a/frontend/components.d.ts b/frontend/components.d.ts deleted file mode 100644 index 540e3917..00000000 --- a/frontend/components.d.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* eslint-disable */ -/* prettier-ignore */ -// @ts-nocheck -// Generated by unplugin-vue-components -// Read more: https://github.com/vuejs/core/pull/3399 -import '@vue/runtime-core' - -export {} - -declare module '@vue/runtime-core' { - export interface GlobalComponents { - ElAside: typeof import('element-plus-secondary/es')['ElAside'] - ElAvatar: typeof import('element-plus-secondary/es')['ElAvatar'] - ElButton: typeof import('element-plus-secondary/es')['ElButton'] - ElCheckbox: typeof import('element-plus-secondary/es')['ElCheckbox'] - ElCheckboxGroup: typeof import('element-plus-secondary/es')['ElCheckboxGroup'] - ElCol: typeof import('element-plus-secondary/es')['ElCol'] - ElContainer: typeof import('element-plus-secondary/es')['ElContainer'] - ElDialog: typeof import('element-plus-secondary/es')['ElDialog'] - ElDivider: typeof import('element-plus-secondary/es')['ElDivider'] - ElDropdown: typeof import('element-plus-secondary/es')['ElDropdown'] - ElDropdownItem: typeof import('element-plus-secondary/es')['ElDropdownItem'] - ElDropdownMenu: typeof import('element-plus-secondary/es')['ElDropdownMenu'] - ElFooter: typeof import('element-plus-secondary/es')['ElFooter'] - ElForm: typeof import('element-plus-secondary/es')['ElForm'] - ElFormItem: typeof import('element-plus-secondary/es')['ElFormItem'] - ElHeader: typeof import('element-plus-secondary/es')['ElHeader'] - ElIcon: typeof import('element-plus-secondary/es')['ElIcon'] - ElInput: typeof import('element-plus-secondary/es')['ElInput'] - ElMain: typeof import('element-plus-secondary/es')['ElMain'] - ElMenu: typeof import('element-plus-secondary/es')['ElMenu'] - ElMenuItem: typeof import('element-plus-secondary/es')['ElMenuItem'] - ElOption: typeof import('element-plus-secondary/es')['ElOption'] - ElRadio: typeof import('element-plus-secondary/es')['ElRadio'] - ElRadioGroup: typeof import('element-plus-secondary/es')['ElRadioGroup'] - ElRow: typeof import('element-plus-secondary/es')['ElRow'] - ElScrollbar: typeof import('element-plus-secondary/es')['ElScrollbar'] - ElSelect: typeof import('element-plus-secondary/es')['ElSelect'] - ElStep: typeof import('element-plus-secondary/es')['ElStep'] - ElSteps: typeof import('element-plus-secondary/es')['ElSteps'] - ElTable: typeof import('element-plus-secondary/es')['ElTable'] - ElTableColumn: typeof import('element-plus-secondary/es')['ElTableColumn'] - ElTabPane: typeof import('element-plus-secondary/es')['ElTabPane'] - ElTabs: typeof import('element-plus-secondary/es')['ElTabs'] - ElTooltip: typeof import('element-plus-secondary/es')['ElTooltip'] - ElUpload: typeof import('element-plus-secondary/es')['ElUpload'] - Icon: typeof import('./src/components/icon-custom/src/Icon.vue')['default'] - Layout: typeof import('./src/components/layout/index.vue')['default'] - RouterLink: typeof import('vue-router')['RouterLink'] - RouterView: typeof import('vue-router')['RouterView'] - SinglePage: typeof import('./src/components/layout/SinglePage.vue')['default'] - } - export interface ComponentCustomProperties { - vLoading: typeof import('element-plus-secondary/es')['ElLoadingDirective'] - } -} diff --git a/frontend/src/api/chat.ts b/frontend/src/api/chat.ts index 91233def..c7d7e28a 100644 --- a/frontend/src/api/chat.ts +++ b/frontend/src/api/chat.ts @@ -147,6 +147,14 @@ function startChat(data: any): Promise { return request.post('/chat/start', data) } +function renameChat(chat_id: number, brief: string): Promise { + return request.post('/chat/rename', {id: chat_id, brief: brief}) +} + +function deleteChat(id: number): Promise { + return request.get(`/chat/delete/${id}`) +} + export const chatApi = { toChatRecord, toChatRecordList, @@ -154,5 +162,7 @@ export const chatApi = { toChatInfoList, list, get, - startChat + startChat, + renameChat, + deleteChat } \ No newline at end of file diff --git a/frontend/src/components/layout/index.vue b/frontend/src/components/layout/index.vue index af27087c..5a8e2bc2 100644 --- a/frontend/src/components/layout/index.vue +++ b/frontend/src/components/layout/index.vue @@ -3,59 +3,67 @@ + + + + \ No newline at end of file diff --git a/frontend/src/views/chat/index.vue b/frontend/src/views/chat/index.vue index c1aa4f20..0cb849dc 100644 --- a/frontend/src/views/chat/index.vue +++ b/frontend/src/views/chat/index.vue @@ -11,29 +11,26 @@ - -
- {{ chat.create_time }}
- {{ chat.brief }} -
-
+
- -
- {{ message.role }}: {{ message.content }} -
- - - + + +
+ {{ message.role }}: {{ message.content }} +
+ + + +
-
+
@@ -66,7 +63,8 @@ import {ref, computed, nextTick, watch, onMounted} from 'vue' import {Plus, Position} from '@element-plus/icons-vue' import {Chat, chatApi, ChatInfo, ChatRecord, questionApi} from '@/api/chat' -import {datasourceApi} from "@/api/datasource.ts"; +import {datasourceApi} from "@/api/datasource.ts" +import ChatList from './ChatList.vue' interface ChatMessage { role: 'user' | 'assistant' @@ -132,6 +130,7 @@ const computedMessages = computed>(() => { const createNewChat = () => { currentChat.value = new ChatInfo() currentChatId.value = undefined + inputMessage.value = '' listDs() } @@ -164,9 +163,28 @@ function onClickHistory(chat: Chat) { } } +function onChatDeleted(id: number) { + for (let i = 0; i < chatList.value.length; i++) { + if (chatList.value[i].id === id) { + chatList.value.splice(i, 1) + return + } + } +} + +function onChatRenamed(chat: Chat) { + chatList.value.forEach((c: Chat) => { + if (c.id === chat.id) { + c.brief = chat.brief + } + }) + if (currentChat.value.id === chat.id) { + currentChat.value.brief = chat.brief + } +} + function listDs() { datasourceApi.list().then((res) => { - console.log(res) dsList.value = res }) } @@ -197,6 +215,7 @@ const sendMessage = async () => { currentRecord.answer = '' currentChat.value.records.push(currentRecord) + inputMessage.value = '' let error = false if (currentChatId.value === undefined) { @@ -221,7 +240,6 @@ const sendMessage = async () => { loading.value = false }) } - if (error) return try { @@ -310,6 +328,8 @@ const handleCtrlEnter = (e: KeyboardEvent) => { border-right: solid 1px rgba(0, 0, 0, 0.3); + background: var(--ed-fill-color-blank); + .chat-container-right-container { height: 100%; @@ -320,12 +340,16 @@ const handleCtrlEnter = (e: KeyboardEvent) => { } .chat-list { - padding-top: unset; + padding: 0 0 20px 0; } } } + .chat-record-list { + padding: 20px 0 0 0; + } + } \ No newline at end of file From e66fa42c21627b750fe6a64d9dc74d78daf82929 Mon Sep 17 00:00:00 2001 From: dataeaseShu Date: Mon, 26 May 2025 15:28:22 +0800 Subject: [PATCH 2/9] feat: Style optimization --- frontend/src/views/work/DatasourceCard.vue | 53 ++++++++++ frontend/src/views/work/index.vue | 116 +++++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 frontend/src/views/work/DatasourceCard.vue create mode 100644 frontend/src/views/work/index.vue diff --git a/frontend/src/views/work/DatasourceCard.vue b/frontend/src/views/work/DatasourceCard.vue new file mode 100644 index 00000000..c5757997 --- /dev/null +++ b/frontend/src/views/work/DatasourceCard.vue @@ -0,0 +1,53 @@ + + + + + diff --git a/frontend/src/views/work/index.vue b/frontend/src/views/work/index.vue new file mode 100644 index 00000000..1ebe488a --- /dev/null +++ b/frontend/src/views/work/index.vue @@ -0,0 +1,116 @@ + + + + + + + From 1554ca3af26425744ae984aceec6031623e7057a Mon Sep 17 00:00:00 2001 From: junjun Date: Mon, 26 May 2025 15:33:53 +0800 Subject: [PATCH 3/9] feat(datasource): datasource UI --- frontend/src/views/ds/form.vue | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/src/views/ds/form.vue b/frontend/src/views/ds/form.vue index df8620e5..29f6015d 100644 --- a/frontend/src/views/ds/form.vue +++ b/frontend/src/views/ds/form.vue @@ -1,7 +1,7 @@