Skip to content

feat: chat message ui #3275

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions apps/chat/serializers/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ def re_open_chat_work_flow(self, chat_id, application):


class OpenChatSerializers(serializers.Serializer):
workspace_id = serializers.CharField(required=True)
workspace_id = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("Workspace ID"))
application_id = serializers.UUIDField(required=True)
chat_user_id = serializers.CharField(required=True, label=_("Client id"))
chat_user_type = serializers.CharField(required=True, label=_("Client Type"))
Expand All @@ -325,7 +325,10 @@ def is_valid(self, *, raise_exception=False):
super().is_valid(raise_exception=True)
workspace_id = self.data.get('workspace_id')
application_id = self.data.get('application_id')
if not QuerySet(Application).filter(id=application_id, workspace_id=workspace_id).exists():
query_set = QuerySet(Application).filter(id=application_id)
if workspace_id:
query_set = query_set.filter(workspace_id=workspace_id)
if not query_set.exists():
raise AppApiException(500, gettext('Application does not exist'))

def open(self):
Expand Down
5 changes: 3 additions & 2 deletions apps/chat/serializers/chat_authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ def profile(self, with_valid=True):
'user_avatar': application_setting.user_avatar,
'show_user_avatar': application_setting.show_user_avatar,
'float_location': application_setting.float_location}
base_node = [node for node in ((application.work_flow or {}).get('nodes', []) or []) if
node.get('id') == 'base-node']
return {**ApplicationSerializerModel(application).data,
'stt_model_id': application.stt_model_id,
'tts_model_id': application.tts_model_id,
Expand All @@ -136,8 +138,7 @@ def profile(self, with_valid=True):
'stt_autosend': application.stt_autosend,
'file_upload_enable': application.file_upload_enable,
'file_upload_setting': application.file_upload_setting,
'work_flow': {'nodes': [node for node in ((application.work_flow or {}).get('nodes', []) or []) if
node.get('id') == 'base-node']},
'work_flow': {'nodes': base_node} if base_node else None,
'show_source': application_access_token.show_source,
'language': application_access_token.language,
**application_setting_dict}
10 changes: 5 additions & 5 deletions apps/chat/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
app_name = 'chat'

urlpatterns = [
path('chat/embed', views.ChatEmbedView.as_view()),
path('application/anonymous_authentication', views.AnonymousAuthentication.as_view()),
path('auth/profile', views.AuthProfile.as_view()),
path('profile', views.ApplicationProfile.as_view()),
path('embed', views.ChatEmbedView.as_view()),
path('auth/anonymous', views.AnonymousAuthentication.as_view()),
path('profile', views.AuthProfile.as_view()),
path('application/profile', views.ApplicationProfile.as_view()),
path('chat_message/<str:chat_id>', views.ChatView.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>/open', views.OpenView.as_view())
path('open', views.OpenView.as_view())
]
10 changes: 6 additions & 4 deletions apps/chat/views/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ def post(self, request: Request, chat_id: str):
return ChatSerializers(data={'chat_id': chat_id,
'chat_user_id': request.auth.chat_user_id,
'chat_user_type': request.auth.chat_user_type,
'application_id': request.auth.application_id}
'application_id': request.auth.application_id,
'debug': False}
).chat(request.data)


Expand All @@ -116,7 +117,8 @@ class OpenView(APIView):
responses=None,
tags=[_('Chat')] # type: ignore
)
def get(self, request: Request, workspace_id: str, application_id: str):
def get(self, request: Request):
return result.success(OpenChatSerializers(
data={'workspace_id': workspace_id, 'application_id': application_id,
'chat_user_id': request.auth.chat_user_id, 'chat_user_type': request.auth.chat_user_type}).open())
data={'application_id': request.auth.application_id,
'chat_user_id': request.auth.chat_user_id, 'chat_user_type': request.auth.chat_user_type,
'debug': False}).open())
42 changes: 28 additions & 14 deletions ui/src/api/chat/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
download,
exportFile,
} from '@/request/chat/index'

import { type ChatProfile } from '@/api/type/chat'
import { type Ref } from 'vue'

import useStore from '@/stores'
Expand All @@ -27,11 +27,9 @@ Object.defineProperty(prefix, 'value', {
* @param loading 加载器
* @returns
*/
const open: (application_id: string, loading?: Ref<boolean>) => Promise<Result<string>> = (
application_id,
loading,
) => {
return get(`${prefix.value}/${application_id}/open`, {}, loading)
const open: (loading?: Ref<boolean>) => Promise<Result<string>> = (loading) => {
return get('/open', {}, loading)

}
/**
* 对话
Expand All @@ -40,22 +38,38 @@ const open: (application_id: string, loading?: Ref<boolean>) => Promise<Result<s
* data
*/
const chat: (chat_id: string, data: any) => Promise<any> = (chat_id, data) => {
return postStream(`/api/chat_message/${chat_id}`, data)
return postStream(`/chat/api/chat_message/${chat_id}`, data)
}
const chatProfile: (assessToken: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
const chatProfile: (assessToken: string, loading?: Ref<boolean>) => Promise<Result<ChatProfile>> = (
assessToken,
loading,
) => {
return get('/auth/profile', { access_token: assessToken }, loading)
return get('/profile', { access_token: assessToken }, loading)
}
const applicationProfile: (assessToken: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
assessToken,
loading,
) => {
return get('/chat/api/profile')
/**
* 匿名认证
* @param assessToken
* @param loading
* @returns
*/
const anonymousAuthentication: (
assessToken: string,
loading?: Ref<boolean>,
) => Promise<Result<any>> = (assessToken, loading) => {
return post('/auth/anonymous', { access_token: assessToken }, {}, loading)
}
/**
* 获取应用相关信息
* @param loading
* @returns
*/
const applicationProfile: (loading?: Ref<boolean>) => Promise<Result<any>> = (loading) => {
return get('/application/profile', {}, loading)
}
export default {
open,
chat,
chatProfile,
anonymousAuthentication,
applicationProfile,
}
15 changes: 15 additions & 0 deletions ui/src/api/type/chat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
interface ChatProfile {
// 是否开启认证
authentication: boolean
// icon
icon?: string
// 应用名称
application_name?: string
// 背景图
bg_icon?: string
// 认证类型
authentication_type?: 'password' | 'login'
// 登录类型
login_value?: Array<string>
}
export { type ChatProfile }
48 changes: 30 additions & 18 deletions ui/src/components/ai-chat/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,10 @@
</div>
</template>
<script setup lang="ts">
import { ref, nextTick, computed, watch, reactive, onMounted, onBeforeUnmount } from 'vue'
import { type Ref, ref, nextTick, computed, watch, reactive, onMounted, onBeforeUnmount } from 'vue'
import { useRoute } from 'vue-router'
import applicationApi from '@/api/application/application'
import chatAPI from '@/api/chat/chat'
import chatLogApi from '@/api/application/chat-log'
import { ChatManagement, type chatType } from '@/api/type/application'
import { randomId } from '@/utils/utils'
Expand Down Expand Up @@ -280,23 +281,35 @@ const handleDebounceClick = debounce((val, other_params_data?: any, chat?: chatT
*/
const openChatId: () => Promise<string> = () => {
const obj = props.applicationDetails
return getOpenChatAPI()(obj.id)
.then((res) => {
chartOpenId.value = res.data
return res.data
})
.catch((res) => {
if (res.response.status === 403) {
return application.asyncAppAuthentication(accessToken).then(() => {
return openChatId()
})
}
return Promise.reject(res)
})
}

const getChatMessageAPI = () => {
if (props.type === 'debug-ai-chat') {
return applicationApi
.open(obj.id)
.then((res) => {
chartOpenId.value = res.data
return res.data
})
.catch((res) => {
if (res.response.status === 403) {
return application.asyncAppAuthentication(accessToken).then(() => {
return openChatId()
})
}
return Promise.reject(res)
})
return applicationApi.chat
} else {
return chatAPI.chat
}
}
const getOpenChatAPI = () => {
if (props.type === 'debug-ai-chat') {
return applicationApi.open
} else {
return Promise.reject('暂不支持')
return (a?: string, loading?: Ref<boolean>) => {
return chatAPI.open(loading)
}
}
}
/**
Expand Down Expand Up @@ -453,8 +466,7 @@ function chatMessage(chat?: any, problem?: string, re_chat?: boolean, other_para
},
}
// 对话
applicationApi
.chat(chartOpenId.value, obj)
getChatMessageAPI()(chartOpenId.value, obj)
.then((response) => {
if (response.status === 401) {
application
Expand Down
16 changes: 8 additions & 8 deletions ui/src/request/chat/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ instance.interceptors.request.use(
if (config.headers === undefined) {
config.headers = new AxiosHeaders()
}
const { user, login } = useStore()
const token = login.getToken()
const language = user.getLanguage()
config.headers['Accept-Language'] = `${language}`
const { chatUser } = useStore()
const token = chatUser.getToken()
// const language = chatUser.getLanguage()
// config.headers['Accept-Language'] = `${language}`
if (token) {
config.headers['AUTHORIZATION'] = `Bearer ${token}`
}
Expand Down Expand Up @@ -203,14 +203,14 @@ export const postStream: (url: string, data?: unknown) => Promise<Result<any> |
url,
data,
) => {
const { user, login } = useStore()
const token = login.getToken()
const language = user.getLanguage()
const { chatUser } = useStore()
const token = chatUser.getToken()
// const language = user.getLanguage()
const headers: HeadersInit = { 'Content-Type': 'application/json' }
if (token) {
headers['AUTHORIZATION'] = `Bearer ${token}`
}
headers['Accept-Language'] = `${language}`
// headers['Accept-Language'] = `${language}`
return fetch(url, {
method: 'POST',
body: data ? JSON.stringify(data) : undefined,
Expand Down
37 changes: 25 additions & 12 deletions ui/src/router/chat/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,39 @@ const router = createRouter({
router.beforeEach(
async (to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => {
NProgress.start()
if (to.name === '404') {
if (to.path === '/404') {
next()
return
}
const { user, login } = useStore()

const notAuthRouteNameList = ['login', 'ForgotPassword', 'ResetPassword', 'Chat', 'UserLogin']
const { chatUser } = useStore()
const notAuthRouteNameList = ['UserLogin']
if (!notAuthRouteNameList.includes(to.name ? to.name.toString() : '')) {
if (to.query && to.query.token) {
localStorage.setItem('token', to.query.token.toString())
}
const token = login.getToken()
if (!token) {
if (to.params && to.params.accessToken) {
chatUser.setAccessToken(to.params.accessToken.toString())
} else {
next({
path: '/login',
path: '/404',
})
return
}
if (!user.userInfo) {
await user.profile()
const token = chatUser.getToken()
const authentication = await chatUser.isAuthentication()
if (authentication) {
if (!token) {
next({
name: 'UserLogin',
params: {
accessToken: to.params.accessToken,
},
})
return
}
} else {
await chatUser.anonymousAuthentication()
}

if (!chatUser.application) {
await chatUser.applicationProfile()
}
}
// 判断是否有菜单权限
Expand Down
10 changes: 8 additions & 2 deletions ui/src/router/chat/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ export const routes: Array<RouteRecordRaw> = [
// 对话
{
path: '/:accessToken',
name: 'Chat',
name: 'chat',
component: () => import('@/views/chat/index.vue'),
},
// 对话用户登录
{
path: '/user-login/:accessToken',
name: 'UserLogin',
name: 'user_login',
component: () => import('@/views/chat/user-login/index.vue'),
},
// 对话用户登录
{
path: '/404',
name: '404',
component: () => import('@/views/404/index.vue'),
},
]
2 changes: 2 additions & 0 deletions ui/src/stores/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import useParagraphStore from './modules/paragraph'
import useDocumentStore from './modules/document'
import useApplicationStore from './modules/application'
import useChatLogStore from './modules/chat-log'
import useChatUserStore from './modules/chat-user'
const useStore = () => ({
common: useCommonStore(),
login: useLoginStore(),
Expand All @@ -25,6 +26,7 @@ const useStore = () => ({
document: useDocumentStore(),
application: useApplicationStore(),
chatLog: useChatLogStore(),
chatUser: useChatUserStore(),
})

export default useStore
Loading
Loading