Skip to content

Commit 34842e4

Browse files
authored
feat: application chat api (#3574)
1 parent 4ffd80f commit 34842e4

File tree

6 files changed

+112
-96
lines changed

6 files changed

+112
-96
lines changed

apps/application/flow/step_node/ai_chat_step_node/impl/base_chat_node.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,9 @@ def write_context(node_variable: Dict, workflow_variable: Dict, node: INode, wor
153153
reasoning_result = reasoning.get_reasoning_content(response)
154154
reasoning_result_end = reasoning.get_end_reasoning_content()
155155
content = reasoning_result.get('content') + reasoning_result_end.get('content')
156-
if 'reasoning_content' in response.response_metadata:
157-
reasoning_content = response.response_metadata.get('reasoning_content', '')
156+
meta = {**response.response_metadata, **response.additional_kwargs}
157+
if 'reasoning_content' in meta:
158+
reasoning_content = meta.get('reasoning_content', '')
158159
else:
159160
reasoning_content = reasoning_result.get('reasoning_content') + reasoning_result_end.get('reasoning_content')
160161
_write_context(node_variable, workflow_variable, node, workflow, content, reasoning_content)

apps/chat/urls.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
path('embed', views.ChatEmbedView.as_view()),
99
path('auth/anonymous', views.AnonymousAuthentication.as_view()),
1010
path('profile', views.AuthProfile.as_view()),
11-
path('application/profile', views.ApplicationProfile.as_view()),
12-
path('chat_message/<str:chat_id>', views.ChatView.as_view()),
13-
path('open', views.OpenView.as_view()),
11+
path('application/profile', views.ApplicationProfile.as_view(), name='profile'),
12+
path('chat_message/<str:chat_id>', views.ChatView.as_view(), name='chat'),
13+
path('open', views.OpenView.as_view(), name='open'),
1414
path('text_to_speech', views.TextToSpeech.as_view()),
1515
path('speech_to_text', views.SpeechToText.as_view()),
1616
path('captcha', views.CaptchaView.as_view(), name='captcha'),

apps/common/cache_data/application_api_key_cache.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
use_get_data=lambda secret_key, use_get_data: use_get_data,
1919
version=Cache_Version.APPLICATION_API_KEY.get_version())
2020
def get_application_api_key(secret_key, use_get_data):
21-
application_api_key = QuerySet(ApplicationApiKey).filter(secret_key=secret_key).first()
21+
application_api_key = QuerySet(ApplicationApiKey).filter(secret_key=secret_key[7:]).first()
2222
return {'allow_cross_domain': application_api_key.allow_cross_domain,
2323
'cross_domain_list': application_api_key.cross_domain_list}
2424

apps/common/init/init_doc.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# coding=utf-8
2+
"""
3+
@project: maxkb
4+
@Author:虎
5+
@file: init_doc.py
6+
@date:2024/5/24 14:11
7+
@desc:
8+
"""
9+
import hashlib
10+
11+
from django.urls import path, URLPattern
12+
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView, SpectacularRedocView
13+
14+
from maxkb.const import CONFIG
15+
16+
chat_api_prefix = CONFIG.get_chat_path()[1:] + '/api/'
17+
18+
19+
def init_app_doc(system_urlpatterns):
20+
system_urlpatterns += [
21+
path('schema/', SpectacularAPIView.as_view(), name='schema'), # schema的配置文件的路由,下面两个ui也是根据这个配置文件来生成的
22+
path('doc/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'), # swagger-ui的路由
23+
path('redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'), # redoc的路由
24+
]
25+
26+
27+
def init_chat_doc(system_urlpatterns, chat_urlpatterns):
28+
system_urlpatterns += [
29+
path('doc_chat_schema/',
30+
SpectacularAPIView.as_view(patterns=[
31+
URLPattern(pattern=f'{chat_api_prefix}{str(url.pattern)}', callback=url.callback,
32+
default_args=url.default_args,
33+
name=url.name) for url in chat_urlpatterns if
34+
['chat', 'open', 'profile'].__contains__(url.name)]),
35+
name='chat_schema'), # schema的配置文件的路由,下面两个ui也是根据这个配置文件来生成的
36+
path('doc_chat/', SpectacularSwaggerView.as_view(url_name='chat_schema'), name='swagger-ui'), # swagger-ui的路由
37+
path('redoc_chat/', SpectacularRedocView.as_view(url_name='chat_schema'), name='redoc'), # redoc的路由
38+
]
39+
40+
41+
def encrypt(text):
42+
md5 = hashlib.md5()
43+
md5.update(text.encode())
44+
result = md5.hexdigest()
45+
return result
46+
47+
48+
def get_call(application_urlpatterns, patterns, params, func):
49+
def run():
50+
if params['valid']():
51+
func(*params['get_params'](application_urlpatterns, patterns))
52+
53+
return run
54+
55+
56+
init_list = [(init_app_doc, {'valid': lambda: CONFIG.get('DOC_PASSWORD') is not None and encrypt(
57+
CONFIG.get('DOC_PASSWORD')) == 'd4fc097197b4b90a122b92cbd5bbe867',
58+
'get_call': get_call,
59+
'get_params': lambda application_urlpatterns, patterns: (application_urlpatterns,)}),
60+
(init_chat_doc, {'valid': lambda: CONFIG.get('DOC_PASSWORD') is not None and encrypt(
61+
CONFIG.get('DOC_PASSWORD')) == 'd4fc097197b4b90a122b92cbd5bbe867' or True, 'get_call': get_call,
62+
'get_params': lambda application_urlpatterns, patterns: (
63+
application_urlpatterns, patterns)})]
64+
65+
66+
def init_doc(system_urlpatterns, chat_patterns):
67+
for init, params in init_list:
68+
if params['valid']():
69+
get_call(system_urlpatterns, chat_patterns, params, init)()

apps/maxkb/urls.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@
2121
from django.templatetags.static import static as _static
2222
from django.urls import path, re_path, include
2323
from django.views import static
24-
from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView
2524
from rest_framework import status
2625

26+
from chat.urls import urlpatterns as chat_urlpatterns
27+
from common.init.init_doc import init_doc
2728
from common.result import Result
2829
from maxkb import settings
2930
from maxkb.conf import PROJECT_DIR
@@ -47,11 +48,7 @@
4748
path(f'{admin_ui_prefix[1:]}/', include('oss.retrieval_urls')),
4849
path(f'{chat_ui_prefix[1:]}/', include('oss.retrieval_urls')),
4950
]
50-
urlpatterns += [
51-
path('schema/', SpectacularAPIView.as_view(), name='schema'), # schema的配置文件的路由,下面两个ui也是根据这个配置文件来生成的
52-
path('doc/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'), # swagger-ui的路由
53-
path('redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'), # redoc的路由
54-
]
51+
init_doc(urlpatterns, chat_urlpatterns)
5552
urlpatterns.append(
5653
re_path(r'^static/(?P<path>.*)$', static.serve, {'document_root': settings.STATIC_ROOT}, name='static'),
5754
)

ui/src/views/application-overview/index.vue

Lines changed: 33 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,12 @@
2121
<el-row :gutter="12">
2222
<el-col :span="12" class="mt-16">
2323
<div class="flex">
24-
<el-text type="info"
25-
>{{ $t('views.applicationOverview.appInfo.publicAccessLink') }}
24+
<el-text type="info">{{ $t('views.applicationOverview.appInfo.publicAccessLink') }}
2625
</el-text>
27-
<el-switch
28-
v-model="accessToken.is_active"
29-
class="ml-8"
30-
size="small"
31-
inline-prompt
26+
<el-switch v-model="accessToken.is_active" class="ml-8" size="small" inline-prompt
3227
:active-text="$t('views.applicationOverview.appInfo.openText')"
3328
:inactive-text="$t('views.applicationOverview.appInfo.closeText')"
34-
:before-change="() => changeState(accessToken.is_active)"
35-
/>
29+
:before-change="() => changeState(accessToken.is_active)" />
3630
</div>
3731

3832
<div class="mt-4 mb-16 url-height flex align-center" style="margin-bottom: 37px">
@@ -45,38 +39,25 @@
4539
</el-button>
4640
</el-tooltip>
4741
<el-tooltip effect="dark" :content="$t('common.refresh')" placement="top">
48-
<el-button
49-
@click="refreshAccessToken"
50-
type="primary"
51-
text
52-
style="margin-left: 1px"
53-
>
42+
<el-button @click="refreshAccessToken" type="primary" text style="margin-left: 1px">
5443
<el-icon>
5544
<RefreshRight />
5645
</el-icon>
5746
</el-button>
5847
</el-tooltip>
5948
</div>
6049
<div>
61-
<el-button
62-
v-if="accessToken?.is_active"
63-
:disabled="!accessToken?.is_active"
64-
tag="a"
65-
:href="shareUrl"
66-
target="_blank"
67-
>
50+
<el-button v-if="accessToken?.is_active" :disabled="!accessToken?.is_active" tag="a" :href="shareUrl"
51+
target="_blank">
6852
<AppIcon iconName="app-create-chat" class="mr-4"></AppIcon>
6953
{{ $t('views.application.operation.toChat') }}
7054
</el-button>
7155
<el-button v-else :disabled="!accessToken?.is_active">
7256
<AppIcon iconName="app-create-chat" class="mr-4"></AppIcon>
7357
{{ $t('views.application.operation.toChat') }}
7458
</el-button>
75-
<el-button
76-
:disabled="!accessToken?.is_active"
77-
@click="openDialog"
78-
v-if="permissionPrecise.overview_embed(id)"
79-
>
59+
<el-button :disabled="!accessToken?.is_active" @click="openDialog"
60+
v-if="permissionPrecise.overview_embed(id)">
8061
<AppIcon iconName="app-export" class="mr-4"></AppIcon>
8162
{{ $t('views.applicationOverview.appInfo.embedInWebsite') }}
8263
</el-button>
@@ -88,10 +69,7 @@
8869
{{ $t('views.applicationOverview.appInfo.accessControl') }}
8970
</el-button>
9071
<!-- 显示设置 -->
91-
<el-button
92-
@click="openDisplaySettingDialog"
93-
v-if="permissionPrecise.overview_display(id)"
94-
>
72+
<el-button @click="openDisplaySettingDialog" v-if="permissionPrecise.overview_display(id)">
9573
<el-icon class="mr-4">
9674
<Setting />
9775
</el-icon>
@@ -101,19 +79,13 @@
10179
</el-col>
10280
<el-col :span="12" class="mt-16">
10381
<div class="flex">
104-
<el-text type="info"
105-
>{{ $t('views.applicationOverview.appInfo.apiAccessCredentials') }}
82+
<el-text type="info">{{ $t('views.applicationOverview.appInfo.apiAccessCredentials') }}
10683
</el-text>
10784
</div>
10885
<div class="mt-4 mb-16 url-height">
10986
<div>
11087
<el-text>API {{ $t('common.fileUpload.document') }}: </el-text>
111-
<el-button
112-
type="primary"
113-
link
114-
@click="toUrl(apiUrl)"
115-
class="vertical-middle lighter break-all"
116-
>
88+
<el-button type="primary" link @click="toUrl(apiUrl)" class="vertical-middle lighter break-all">
11789
{{ apiUrl }}
11890
</el-button>
11991
</div>
@@ -124,7 +96,7 @@
12496

12597
<span class="vertical-middle lighter break-all ellipsis-1">{{
12698
baseUrl + id
127-
}}</span>
99+
}}</span>
128100
<el-tooltip effect="dark" :content="$t('common.copy')" placement="top">
129101
<el-button type="primary" text @click="copyClick(baseUrl + id)">
130102
<AppIcon iconName="app-copy"></AppIcon>
@@ -133,10 +105,7 @@
133105
</div>
134106
</div>
135107
<div>
136-
<el-button
137-
@click="openAPIKeyDialog"
138-
v-if="permissionPrecise.overview_api_key(id)"
139-
>
108+
<el-button @click="openAPIKeyDialog" v-if="permissionPrecise.overview_api_key(id)">
140109
<el-icon class="mr-4">
141110
<Key />
142111
</el-icon>
@@ -152,29 +121,13 @@
152121
{{ $t('views.applicationOverview.monitor.monitoringStatistics') }}
153122
</h4>
154123
<div class="mb-16">
155-
<el-select
156-
v-model="history_day"
157-
class="mr-12"
158-
@change="changeDayHandle"
159-
style="width: 180px"
160-
>
161-
<el-option
162-
v-for="item in dayOptions"
163-
:key="item.value"
164-
:label="item.label"
165-
:value="item.value"
166-
/>
124+
<el-select v-model="history_day" class="mr-12" @change="changeDayHandle" style="width: 180px">
125+
<el-option v-for="item in dayOptions" :key="item.value" :label="item.label" :value="item.value" />
167126
</el-select>
168-
<el-date-picker
169-
v-if="history_day === 'other'"
170-
v-model="daterangeValue"
171-
type="daterange"
127+
<el-date-picker v-if="history_day === 'other'" v-model="daterangeValue" type="daterange"
172128
:start-placeholder="$t('views.applicationOverview.monitor.startDatePlaceholder')"
173-
:end-placeholder="$t('views.applicationOverview.monitor.endDatePlaceholder')"
174-
format="YYYY-MM-DD"
175-
value-format="YYYY-MM-DD"
176-
@change="changeDayRangeHandle"
177-
/>
129+
:end-placeholder="$t('views.applicationOverview.monitor.endDatePlaceholder')" format="YYYY-MM-DD"
130+
value-format="YYYY-MM-DD" @change="changeDayRangeHandle" />
178131
</div>
179132
<div v-loading="statisticsLoading">
180133
<StatisticsCharts :data="statisticsData" />
@@ -183,11 +136,7 @@
183136
</div>
184137
</el-scrollbar>
185138

186-
<EmbedDialog
187-
ref="EmbedDialogRef"
188-
:data="detail"
189-
:api-input-params="mapToUrlParams(apiInputParams)"
190-
/>
139+
<EmbedDialog ref="EmbedDialogRef" :data="detail" :api-input-params="mapToUrlParams(apiInputParams)" />
191140
<APIKeyDialog ref="APIKeyDialogRef" />
192141

193142
<!-- 社区版访问限制 -->
@@ -232,7 +181,7 @@ const {
232181
params: { id },
233182
} = route as any
234183
235-
const apiUrl = window.location.origin + '/doc/chat/'
184+
const apiUrl = window.location.origin + '/doc_chat/'
236185
237186
const baseUrl = window.location.origin + `${window.MaxKB.chatPrefix}/api/`
238187
@@ -373,7 +322,7 @@ function refreshAccessToken() {
373322
const str = t('views.applicationOverview.appInfo.refreshToken.refreshSuccess')
374323
updateAccessToken(obj, str)
375324
})
376-
.catch(() => {})
325+
.catch(() => { })
377326
}
378327
379328
async function changeState(bool: boolean) {
@@ -419,20 +368,20 @@ function getDetail() {
419368
.map((v: any) => {
420369
apiInputParams.value = v.properties.api_input_field_list
421370
? v.properties.api_input_field_list.map((v: any) => {
422-
return {
423-
name: v.variable,
424-
value: v.default_value,
425-
}
426-
})
371+
return {
372+
name: v.variable,
373+
value: v.default_value,
374+
}
375+
})
427376
: v.properties.input_field_list
428377
? v.properties.input_field_list
429-
.filter((v: any) => v.assignment_method === 'api_input')
430-
.map((v: any) => {
431-
return {
432-
name: v.variable,
433-
value: v.default_value,
434-
}
435-
})
378+
.filter((v: any) => v.assignment_method === 'api_input')
379+
.map((v: any) => {
380+
return {
381+
name: v.variable,
382+
value: v.default_value,
383+
}
384+
})
436385
: []
437386
})
438387
})

0 commit comments

Comments
 (0)