feat: application chat api (#3574)

This commit is contained in:
shaohuzhang1 2025-07-12 17:54:37 +08:00 committed by GitHub
parent 4ffd80f184
commit 34842e4ae4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 112 additions and 96 deletions

View File

@ -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_end = reasoning.get_end_reasoning_content()
content = reasoning_result.get('content') + reasoning_result_end.get('content')
if 'reasoning_content' in response.response_metadata:
reasoning_content = response.response_metadata.get('reasoning_content', '')
meta = {**response.response_metadata, **response.additional_kwargs}
if 'reasoning_content' in meta:
reasoning_content = meta.get('reasoning_content', '')
else:
reasoning_content = reasoning_result.get('reasoning_content') + reasoning_result_end.get('reasoning_content')
_write_context(node_variable, workflow_variable, node, workflow, content, reasoning_content)

View File

@ -8,9 +8,9 @@ urlpatterns = [
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('open', views.OpenView.as_view()),
path('application/profile', views.ApplicationProfile.as_view(), name='profile'),
path('chat_message/<str:chat_id>', views.ChatView.as_view(), name='chat'),
path('open', views.OpenView.as_view(), name='open'),
path('text_to_speech', views.TextToSpeech.as_view()),
path('speech_to_text', views.SpeechToText.as_view()),
path('captcha', views.CaptchaView.as_view(), name='captcha'),

View File

@ -18,7 +18,7 @@ from common.utils.cache_util import get_cache
use_get_data=lambda secret_key, use_get_data: use_get_data,
version=Cache_Version.APPLICATION_API_KEY.get_version())
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,
'cross_domain_list': application_api_key.cross_domain_list}

View File

@ -0,0 +1,69 @@
# coding=utf-8
"""
@project: maxkb
@Author
@file init_doc.py
@date2024/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)()

View File

@ -21,9 +21,10 @@ from django.http import HttpResponse, HttpResponseRedirect
from django.templatetags.static import static as _static
from django.urls import path, re_path, include
from django.views import static
from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView
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 maxkb import settings
from maxkb.conf import PROJECT_DIR
@ -47,11 +48,7 @@ urlpatterns = [
path(f'{admin_ui_prefix[1:]}/', include('oss.retrieval_urls')),
path(f'{chat_ui_prefix[1:]}/', include('oss.retrieval_urls')),
]
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的路由
]
init_doc(urlpatterns, chat_urlpatterns)
urlpatterns.append(
re_path(r'^static/(?P<path>.*)$', static.serve, {'document_root': settings.STATIC_ROOT}, name='static'),
)

View File

@ -21,18 +21,12 @@
<el-row :gutter="12">
<el-col :span="12" class="mt-16">
<div class="flex">
<el-text type="info"
>{{ $t('views.applicationOverview.appInfo.publicAccessLink') }}
<el-text type="info">{{ $t('views.applicationOverview.appInfo.publicAccessLink') }}
</el-text>
<el-switch
v-model="accessToken.is_active"
class="ml-8"
size="small"
inline-prompt
<el-switch v-model="accessToken.is_active" class="ml-8" size="small" inline-prompt
:active-text="$t('views.applicationOverview.appInfo.openText')"
:inactive-text="$t('views.applicationOverview.appInfo.closeText')"
:before-change="() => changeState(accessToken.is_active)"
/>
:before-change="() => changeState(accessToken.is_active)" />
</div>
<div class="mt-4 mb-16 url-height flex align-center" style="margin-bottom: 37px">
@ -45,12 +39,7 @@
</el-button>
</el-tooltip>
<el-tooltip effect="dark" :content="$t('common.refresh')" placement="top">
<el-button
@click="refreshAccessToken"
type="primary"
text
style="margin-left: 1px"
>
<el-button @click="refreshAccessToken" type="primary" text style="margin-left: 1px">
<el-icon>
<RefreshRight />
</el-icon>
@ -58,13 +47,8 @@
</el-tooltip>
</div>
<div>
<el-button
v-if="accessToken?.is_active"
:disabled="!accessToken?.is_active"
tag="a"
:href="shareUrl"
target="_blank"
>
<el-button v-if="accessToken?.is_active" :disabled="!accessToken?.is_active" tag="a" :href="shareUrl"
target="_blank">
<AppIcon iconName="app-create-chat" class="mr-4"></AppIcon>
{{ $t('views.application.operation.toChat') }}
</el-button>
@ -72,11 +56,8 @@
<AppIcon iconName="app-create-chat" class="mr-4"></AppIcon>
{{ $t('views.application.operation.toChat') }}
</el-button>
<el-button
:disabled="!accessToken?.is_active"
@click="openDialog"
v-if="permissionPrecise.overview_embed(id)"
>
<el-button :disabled="!accessToken?.is_active" @click="openDialog"
v-if="permissionPrecise.overview_embed(id)">
<AppIcon iconName="app-export" class="mr-4"></AppIcon>
{{ $t('views.applicationOverview.appInfo.embedInWebsite') }}
</el-button>
@ -88,10 +69,7 @@
{{ $t('views.applicationOverview.appInfo.accessControl') }}
</el-button>
<!-- 显示设置 -->
<el-button
@click="openDisplaySettingDialog"
v-if="permissionPrecise.overview_display(id)"
>
<el-button @click="openDisplaySettingDialog" v-if="permissionPrecise.overview_display(id)">
<el-icon class="mr-4">
<Setting />
</el-icon>
@ -101,19 +79,13 @@
</el-col>
<el-col :span="12" class="mt-16">
<div class="flex">
<el-text type="info"
>{{ $t('views.applicationOverview.appInfo.apiAccessCredentials') }}
<el-text type="info">{{ $t('views.applicationOverview.appInfo.apiAccessCredentials') }}
</el-text>
</div>
<div class="mt-4 mb-16 url-height">
<div>
<el-text>API {{ $t('common.fileUpload.document') }} </el-text>
<el-button
type="primary"
link
@click="toUrl(apiUrl)"
class="vertical-middle lighter break-all"
>
<el-button type="primary" link @click="toUrl(apiUrl)" class="vertical-middle lighter break-all">
{{ apiUrl }}
</el-button>
</div>
@ -124,7 +96,7 @@
<span class="vertical-middle lighter break-all ellipsis-1">{{
baseUrl + id
}}</span>
}}</span>
<el-tooltip effect="dark" :content="$t('common.copy')" placement="top">
<el-button type="primary" text @click="copyClick(baseUrl + id)">
<AppIcon iconName="app-copy"></AppIcon>
@ -133,10 +105,7 @@
</div>
</div>
<div>
<el-button
@click="openAPIKeyDialog"
v-if="permissionPrecise.overview_api_key(id)"
>
<el-button @click="openAPIKeyDialog" v-if="permissionPrecise.overview_api_key(id)">
<el-icon class="mr-4">
<Key />
</el-icon>
@ -152,29 +121,13 @@
{{ $t('views.applicationOverview.monitor.monitoringStatistics') }}
</h4>
<div class="mb-16">
<el-select
v-model="history_day"
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 v-model="history_day" 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-date-picker
v-if="history_day === 'other'"
v-model="daterangeValue"
type="daterange"
<el-date-picker v-if="history_day === 'other'" v-model="daterangeValue" type="daterange"
:start-placeholder="$t('views.applicationOverview.monitor.startDatePlaceholder')"
:end-placeholder="$t('views.applicationOverview.monitor.endDatePlaceholder')"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
@change="changeDayRangeHandle"
/>
:end-placeholder="$t('views.applicationOverview.monitor.endDatePlaceholder')" format="YYYY-MM-DD"
value-format="YYYY-MM-DD" @change="changeDayRangeHandle" />
</div>
<div v-loading="statisticsLoading">
<StatisticsCharts :data="statisticsData" />
@ -183,11 +136,7 @@
</div>
</el-scrollbar>
<EmbedDialog
ref="EmbedDialogRef"
:data="detail"
:api-input-params="mapToUrlParams(apiInputParams)"
/>
<EmbedDialog ref="EmbedDialogRef" :data="detail" :api-input-params="mapToUrlParams(apiInputParams)" />
<APIKeyDialog ref="APIKeyDialogRef" />
<!-- 社区版访问限制 -->
@ -232,7 +181,7 @@ const {
params: { id },
} = 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/`
@ -373,7 +322,7 @@ function refreshAccessToken() {
const str = t('views.applicationOverview.appInfo.refreshToken.refreshSuccess')
updateAccessToken(obj, str)
})
.catch(() => {})
.catch(() => { })
}
async function changeState(bool: boolean) {
@ -419,20 +368,20 @@ function getDetail() {
.map((v: any) => {
apiInputParams.value = v.properties.api_input_field_list
? v.properties.api_input_field_list.map((v: any) => {
return {
name: v.variable,
value: v.default_value,
}
})
return {
name: v.variable,
value: v.default_value,
}
})
: v.properties.input_field_list
? v.properties.input_field_list
.filter((v: any) => v.assignment_method === 'api_input')
.map((v: any) => {
return {
name: v.variable,
value: v.default_value,
}
})
.filter((v: any) => v.assignment_method === 'api_input')
.map((v: any) => {
return {
name: v.variable,
value: v.default_value,
}
})
: []
})
})