feat: application chat api (#3574)
This commit is contained in:
parent
4ffd80f184
commit
34842e4ae4
@ -153,8 +153,9 @@ def write_context(node_variable: Dict, workflow_variable: Dict, node: INode, wor
|
|||||||
reasoning_result = reasoning.get_reasoning_content(response)
|
reasoning_result = reasoning.get_reasoning_content(response)
|
||||||
reasoning_result_end = reasoning.get_end_reasoning_content()
|
reasoning_result_end = reasoning.get_end_reasoning_content()
|
||||||
content = reasoning_result.get('content') + reasoning_result_end.get('content')
|
content = reasoning_result.get('content') + reasoning_result_end.get('content')
|
||||||
if 'reasoning_content' in response.response_metadata:
|
meta = {**response.response_metadata, **response.additional_kwargs}
|
||||||
reasoning_content = response.response_metadata.get('reasoning_content', '')
|
if 'reasoning_content' in meta:
|
||||||
|
reasoning_content = meta.get('reasoning_content', '')
|
||||||
else:
|
else:
|
||||||
reasoning_content = reasoning_result.get('reasoning_content') + reasoning_result_end.get('reasoning_content')
|
reasoning_content = reasoning_result.get('reasoning_content') + reasoning_result_end.get('reasoning_content')
|
||||||
_write_context(node_variable, workflow_variable, node, workflow, content, reasoning_content)
|
_write_context(node_variable, workflow_variable, node, workflow, content, reasoning_content)
|
||||||
|
|||||||
@ -8,9 +8,9 @@ urlpatterns = [
|
|||||||
path('embed', views.ChatEmbedView.as_view()),
|
path('embed', views.ChatEmbedView.as_view()),
|
||||||
path('auth/anonymous', views.AnonymousAuthentication.as_view()),
|
path('auth/anonymous', views.AnonymousAuthentication.as_view()),
|
||||||
path('profile', views.AuthProfile.as_view()),
|
path('profile', views.AuthProfile.as_view()),
|
||||||
path('application/profile', views.ApplicationProfile.as_view()),
|
path('application/profile', views.ApplicationProfile.as_view(), name='profile'),
|
||||||
path('chat_message/<str:chat_id>', views.ChatView.as_view()),
|
path('chat_message/<str:chat_id>', views.ChatView.as_view(), name='chat'),
|
||||||
path('open', views.OpenView.as_view()),
|
path('open', views.OpenView.as_view(), name='open'),
|
||||||
path('text_to_speech', views.TextToSpeech.as_view()),
|
path('text_to_speech', views.TextToSpeech.as_view()),
|
||||||
path('speech_to_text', views.SpeechToText.as_view()),
|
path('speech_to_text', views.SpeechToText.as_view()),
|
||||||
path('captcha', views.CaptchaView.as_view(), name='captcha'),
|
path('captcha', views.CaptchaView.as_view(), name='captcha'),
|
||||||
|
|||||||
@ -18,7 +18,7 @@ from common.utils.cache_util import get_cache
|
|||||||
use_get_data=lambda secret_key, use_get_data: use_get_data,
|
use_get_data=lambda secret_key, use_get_data: use_get_data,
|
||||||
version=Cache_Version.APPLICATION_API_KEY.get_version())
|
version=Cache_Version.APPLICATION_API_KEY.get_version())
|
||||||
def get_application_api_key(secret_key, use_get_data):
|
def get_application_api_key(secret_key, use_get_data):
|
||||||
application_api_key = QuerySet(ApplicationApiKey).filter(secret_key=secret_key).first()
|
application_api_key = QuerySet(ApplicationApiKey).filter(secret_key=secret_key[7:]).first()
|
||||||
return {'allow_cross_domain': application_api_key.allow_cross_domain,
|
return {'allow_cross_domain': application_api_key.allow_cross_domain,
|
||||||
'cross_domain_list': application_api_key.cross_domain_list}
|
'cross_domain_list': application_api_key.cross_domain_list}
|
||||||
|
|
||||||
|
|||||||
69
apps/common/init/init_doc.py
Normal file
69
apps/common/init/init_doc.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
"""
|
||||||
|
@project: maxkb
|
||||||
|
@Author:虎
|
||||||
|
@file: init_doc.py
|
||||||
|
@date:2024/5/24 14:11
|
||||||
|
@desc:
|
||||||
|
"""
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
from django.urls import path, URLPattern
|
||||||
|
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView, SpectacularRedocView
|
||||||
|
|
||||||
|
from maxkb.const import CONFIG
|
||||||
|
|
||||||
|
chat_api_prefix = CONFIG.get_chat_path()[1:] + '/api/'
|
||||||
|
|
||||||
|
|
||||||
|
def init_app_doc(system_urlpatterns):
|
||||||
|
system_urlpatterns += [
|
||||||
|
path('schema/', SpectacularAPIView.as_view(), name='schema'), # schema的配置文件的路由,下面两个ui也是根据这个配置文件来生成的
|
||||||
|
path('doc/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'), # swagger-ui的路由
|
||||||
|
path('redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'), # redoc的路由
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def init_chat_doc(system_urlpatterns, chat_urlpatterns):
|
||||||
|
system_urlpatterns += [
|
||||||
|
path('doc_chat_schema/',
|
||||||
|
SpectacularAPIView.as_view(patterns=[
|
||||||
|
URLPattern(pattern=f'{chat_api_prefix}{str(url.pattern)}', callback=url.callback,
|
||||||
|
default_args=url.default_args,
|
||||||
|
name=url.name) for url in chat_urlpatterns if
|
||||||
|
['chat', 'open', 'profile'].__contains__(url.name)]),
|
||||||
|
name='chat_schema'), # schema的配置文件的路由,下面两个ui也是根据这个配置文件来生成的
|
||||||
|
path('doc_chat/', SpectacularSwaggerView.as_view(url_name='chat_schema'), name='swagger-ui'), # swagger-ui的路由
|
||||||
|
path('redoc_chat/', SpectacularRedocView.as_view(url_name='chat_schema'), name='redoc'), # redoc的路由
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def encrypt(text):
|
||||||
|
md5 = hashlib.md5()
|
||||||
|
md5.update(text.encode())
|
||||||
|
result = md5.hexdigest()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_call(application_urlpatterns, patterns, params, func):
|
||||||
|
def run():
|
||||||
|
if params['valid']():
|
||||||
|
func(*params['get_params'](application_urlpatterns, patterns))
|
||||||
|
|
||||||
|
return run
|
||||||
|
|
||||||
|
|
||||||
|
init_list = [(init_app_doc, {'valid': lambda: CONFIG.get('DOC_PASSWORD') is not None and encrypt(
|
||||||
|
CONFIG.get('DOC_PASSWORD')) == 'd4fc097197b4b90a122b92cbd5bbe867',
|
||||||
|
'get_call': get_call,
|
||||||
|
'get_params': lambda application_urlpatterns, patterns: (application_urlpatterns,)}),
|
||||||
|
(init_chat_doc, {'valid': lambda: CONFIG.get('DOC_PASSWORD') is not None and encrypt(
|
||||||
|
CONFIG.get('DOC_PASSWORD')) == 'd4fc097197b4b90a122b92cbd5bbe867' or True, 'get_call': get_call,
|
||||||
|
'get_params': lambda application_urlpatterns, patterns: (
|
||||||
|
application_urlpatterns, patterns)})]
|
||||||
|
|
||||||
|
|
||||||
|
def init_doc(system_urlpatterns, chat_patterns):
|
||||||
|
for init, params in init_list:
|
||||||
|
if params['valid']():
|
||||||
|
get_call(system_urlpatterns, chat_patterns, params, init)()
|
||||||
@ -21,9 +21,10 @@ from django.http import HttpResponse, HttpResponseRedirect
|
|||||||
from django.templatetags.static import static as _static
|
from django.templatetags.static import static as _static
|
||||||
from django.urls import path, re_path, include
|
from django.urls import path, re_path, include
|
||||||
from django.views import static
|
from django.views import static
|
||||||
from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView
|
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
|
||||||
|
from chat.urls import urlpatterns as chat_urlpatterns
|
||||||
|
from common.init.init_doc import init_doc
|
||||||
from common.result import Result
|
from common.result import Result
|
||||||
from maxkb import settings
|
from maxkb import settings
|
||||||
from maxkb.conf import PROJECT_DIR
|
from maxkb.conf import PROJECT_DIR
|
||||||
@ -47,11 +48,7 @@ urlpatterns = [
|
|||||||
path(f'{admin_ui_prefix[1:]}/', include('oss.retrieval_urls')),
|
path(f'{admin_ui_prefix[1:]}/', include('oss.retrieval_urls')),
|
||||||
path(f'{chat_ui_prefix[1:]}/', include('oss.retrieval_urls')),
|
path(f'{chat_ui_prefix[1:]}/', include('oss.retrieval_urls')),
|
||||||
]
|
]
|
||||||
urlpatterns += [
|
init_doc(urlpatterns, chat_urlpatterns)
|
||||||
path('schema/', SpectacularAPIView.as_view(), name='schema'), # schema的配置文件的路由,下面两个ui也是根据这个配置文件来生成的
|
|
||||||
path('doc/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'), # swagger-ui的路由
|
|
||||||
path('redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'), # redoc的路由
|
|
||||||
]
|
|
||||||
urlpatterns.append(
|
urlpatterns.append(
|
||||||
re_path(r'^static/(?P<path>.*)$', static.serve, {'document_root': settings.STATIC_ROOT}, name='static'),
|
re_path(r'^static/(?P<path>.*)$', static.serve, {'document_root': settings.STATIC_ROOT}, name='static'),
|
||||||
)
|
)
|
||||||
|
|||||||
@ -21,18 +21,12 @@
|
|||||||
<el-row :gutter="12">
|
<el-row :gutter="12">
|
||||||
<el-col :span="12" class="mt-16">
|
<el-col :span="12" class="mt-16">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<el-text type="info"
|
<el-text type="info">{{ $t('views.applicationOverview.appInfo.publicAccessLink') }}
|
||||||
>{{ $t('views.applicationOverview.appInfo.publicAccessLink') }}
|
|
||||||
</el-text>
|
</el-text>
|
||||||
<el-switch
|
<el-switch v-model="accessToken.is_active" class="ml-8" size="small" inline-prompt
|
||||||
v-model="accessToken.is_active"
|
|
||||||
class="ml-8"
|
|
||||||
size="small"
|
|
||||||
inline-prompt
|
|
||||||
:active-text="$t('views.applicationOverview.appInfo.openText')"
|
:active-text="$t('views.applicationOverview.appInfo.openText')"
|
||||||
:inactive-text="$t('views.applicationOverview.appInfo.closeText')"
|
:inactive-text="$t('views.applicationOverview.appInfo.closeText')"
|
||||||
:before-change="() => changeState(accessToken.is_active)"
|
:before-change="() => changeState(accessToken.is_active)" />
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-4 mb-16 url-height flex align-center" style="margin-bottom: 37px">
|
<div class="mt-4 mb-16 url-height flex align-center" style="margin-bottom: 37px">
|
||||||
@ -45,12 +39,7 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip effect="dark" :content="$t('common.refresh')" placement="top">
|
<el-tooltip effect="dark" :content="$t('common.refresh')" placement="top">
|
||||||
<el-button
|
<el-button @click="refreshAccessToken" type="primary" text style="margin-left: 1px">
|
||||||
@click="refreshAccessToken"
|
|
||||||
type="primary"
|
|
||||||
text
|
|
||||||
style="margin-left: 1px"
|
|
||||||
>
|
|
||||||
<el-icon>
|
<el-icon>
|
||||||
<RefreshRight />
|
<RefreshRight />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
@ -58,13 +47,8 @@
|
|||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<el-button
|
<el-button v-if="accessToken?.is_active" :disabled="!accessToken?.is_active" tag="a" :href="shareUrl"
|
||||||
v-if="accessToken?.is_active"
|
target="_blank">
|
||||||
:disabled="!accessToken?.is_active"
|
|
||||||
tag="a"
|
|
||||||
:href="shareUrl"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<AppIcon iconName="app-create-chat" class="mr-4"></AppIcon>
|
<AppIcon iconName="app-create-chat" class="mr-4"></AppIcon>
|
||||||
{{ $t('views.application.operation.toChat') }}
|
{{ $t('views.application.operation.toChat') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -72,11 +56,8 @@
|
|||||||
<AppIcon iconName="app-create-chat" class="mr-4"></AppIcon>
|
<AppIcon iconName="app-create-chat" class="mr-4"></AppIcon>
|
||||||
{{ $t('views.application.operation.toChat') }}
|
{{ $t('views.application.operation.toChat') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button :disabled="!accessToken?.is_active" @click="openDialog"
|
||||||
:disabled="!accessToken?.is_active"
|
v-if="permissionPrecise.overview_embed(id)">
|
||||||
@click="openDialog"
|
|
||||||
v-if="permissionPrecise.overview_embed(id)"
|
|
||||||
>
|
|
||||||
<AppIcon iconName="app-export" class="mr-4"></AppIcon>
|
<AppIcon iconName="app-export" class="mr-4"></AppIcon>
|
||||||
{{ $t('views.applicationOverview.appInfo.embedInWebsite') }}
|
{{ $t('views.applicationOverview.appInfo.embedInWebsite') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -88,10 +69,7 @@
|
|||||||
{{ $t('views.applicationOverview.appInfo.accessControl') }}
|
{{ $t('views.applicationOverview.appInfo.accessControl') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<!-- 显示设置 -->
|
<!-- 显示设置 -->
|
||||||
<el-button
|
<el-button @click="openDisplaySettingDialog" v-if="permissionPrecise.overview_display(id)">
|
||||||
@click="openDisplaySettingDialog"
|
|
||||||
v-if="permissionPrecise.overview_display(id)"
|
|
||||||
>
|
|
||||||
<el-icon class="mr-4">
|
<el-icon class="mr-4">
|
||||||
<Setting />
|
<Setting />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
@ -101,19 +79,13 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12" class="mt-16">
|
<el-col :span="12" class="mt-16">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<el-text type="info"
|
<el-text type="info">{{ $t('views.applicationOverview.appInfo.apiAccessCredentials') }}
|
||||||
>{{ $t('views.applicationOverview.appInfo.apiAccessCredentials') }}
|
|
||||||
</el-text>
|
</el-text>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4 mb-16 url-height">
|
<div class="mt-4 mb-16 url-height">
|
||||||
<div>
|
<div>
|
||||||
<el-text>API {{ $t('common.fileUpload.document') }}: </el-text>
|
<el-text>API {{ $t('common.fileUpload.document') }}: </el-text>
|
||||||
<el-button
|
<el-button type="primary" link @click="toUrl(apiUrl)" class="vertical-middle lighter break-all">
|
||||||
type="primary"
|
|
||||||
link
|
|
||||||
@click="toUrl(apiUrl)"
|
|
||||||
class="vertical-middle lighter break-all"
|
|
||||||
>
|
|
||||||
{{ apiUrl }}
|
{{ apiUrl }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
@ -133,10 +105,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<el-button
|
<el-button @click="openAPIKeyDialog" v-if="permissionPrecise.overview_api_key(id)">
|
||||||
@click="openAPIKeyDialog"
|
|
||||||
v-if="permissionPrecise.overview_api_key(id)"
|
|
||||||
>
|
|
||||||
<el-icon class="mr-4">
|
<el-icon class="mr-4">
|
||||||
<Key />
|
<Key />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
@ -152,29 +121,13 @@
|
|||||||
{{ $t('views.applicationOverview.monitor.monitoringStatistics') }}
|
{{ $t('views.applicationOverview.monitor.monitoringStatistics') }}
|
||||||
</h4>
|
</h4>
|
||||||
<div class="mb-16">
|
<div class="mb-16">
|
||||||
<el-select
|
<el-select v-model="history_day" class="mr-12" @change="changeDayHandle" style="width: 180px">
|
||||||
v-model="history_day"
|
<el-option v-for="item in dayOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||||
class="mr-12"
|
|
||||||
@change="changeDayHandle"
|
|
||||||
style="width: 180px"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in dayOptions"
|
|
||||||
:key="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
</el-select>
|
||||||
<el-date-picker
|
<el-date-picker v-if="history_day === 'other'" v-model="daterangeValue" type="daterange"
|
||||||
v-if="history_day === 'other'"
|
|
||||||
v-model="daterangeValue"
|
|
||||||
type="daterange"
|
|
||||||
:start-placeholder="$t('views.applicationOverview.monitor.startDatePlaceholder')"
|
:start-placeholder="$t('views.applicationOverview.monitor.startDatePlaceholder')"
|
||||||
:end-placeholder="$t('views.applicationOverview.monitor.endDatePlaceholder')"
|
:end-placeholder="$t('views.applicationOverview.monitor.endDatePlaceholder')" format="YYYY-MM-DD"
|
||||||
format="YYYY-MM-DD"
|
value-format="YYYY-MM-DD" @change="changeDayRangeHandle" />
|
||||||
value-format="YYYY-MM-DD"
|
|
||||||
@change="changeDayRangeHandle"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-loading="statisticsLoading">
|
<div v-loading="statisticsLoading">
|
||||||
<StatisticsCharts :data="statisticsData" />
|
<StatisticsCharts :data="statisticsData" />
|
||||||
@ -183,11 +136,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
|
|
||||||
<EmbedDialog
|
<EmbedDialog ref="EmbedDialogRef" :data="detail" :api-input-params="mapToUrlParams(apiInputParams)" />
|
||||||
ref="EmbedDialogRef"
|
|
||||||
:data="detail"
|
|
||||||
:api-input-params="mapToUrlParams(apiInputParams)"
|
|
||||||
/>
|
|
||||||
<APIKeyDialog ref="APIKeyDialogRef" />
|
<APIKeyDialog ref="APIKeyDialogRef" />
|
||||||
|
|
||||||
<!-- 社区版访问限制 -->
|
<!-- 社区版访问限制 -->
|
||||||
@ -232,7 +181,7 @@ const {
|
|||||||
params: { id },
|
params: { id },
|
||||||
} = route as any
|
} = route as any
|
||||||
|
|
||||||
const apiUrl = window.location.origin + '/doc/chat/'
|
const apiUrl = window.location.origin + '/doc_chat/'
|
||||||
|
|
||||||
const baseUrl = window.location.origin + `${window.MaxKB.chatPrefix}/api/`
|
const baseUrl = window.location.origin + `${window.MaxKB.chatPrefix}/api/`
|
||||||
|
|
||||||
@ -373,7 +322,7 @@ function refreshAccessToken() {
|
|||||||
const str = t('views.applicationOverview.appInfo.refreshToken.refreshSuccess')
|
const str = t('views.applicationOverview.appInfo.refreshToken.refreshSuccess')
|
||||||
updateAccessToken(obj, str)
|
updateAccessToken(obj, str)
|
||||||
})
|
})
|
||||||
.catch(() => {})
|
.catch(() => { })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function changeState(bool: boolean) {
|
async function changeState(bool: boolean) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user