This commit is contained in:
liqiang-fit2cloud 2025-03-03 15:06:11 +08:00
commit dea8c73cdd
13 changed files with 217 additions and 227 deletions

View File

@ -17,6 +17,7 @@ MaxKB = Max Knowledge Base, it is a chatbot based on Large Language Models (LLM)
- **Flexible Orchestration**: Equipped with a powerful workflow engine and function library, enabling the orchestration of AI processes to meet the needs of complex business scenarios. - **Flexible Orchestration**: Equipped with a powerful workflow engine and function library, enabling the orchestration of AI processes to meet the needs of complex business scenarios.
- **Seamless Integration**: Facilitates zero-coding rapid integration into third-party business systems, quickly equipping existing systems with intelligent Q&A capabilities to enhance user satisfaction. - **Seamless Integration**: Facilitates zero-coding rapid integration into third-party business systems, quickly equipping existing systems with intelligent Q&A capabilities to enhance user satisfaction.
- **Model-Agnostic**: Supports various large models, including private models (such as DeepSeek, Llama, Qwen, etc.) and public models (like OpenAI, Claude, Gemini, etc.). - **Model-Agnostic**: Supports various large models, including private models (such as DeepSeek, Llama, Qwen, etc.) and public models (like OpenAI, Claude, Gemini, etc.).
- **Multi Modal**: Native support for input and output text, image, audio and video.
## Quick start ## Quick start

View File

@ -307,7 +307,11 @@ class BaseChatStep(IChatStep):
return manage.get_base_to_response().to_block_response(str(chat_id), str(chat_record_id), return manage.get_base_to_response().to_block_response(str(chat_id), str(chat_record_id),
content, True, content, True,
request_token, response_token, request_token, response_token,
{'reasoning_content': reasoning_content}) {'reasoning_content': reasoning_content,
'answer_list': [{
'content': content,
'reasoning_content': reasoning_content
}]})
except Exception as e: except Exception as e:
all_text = 'Exception:' + str(e) all_text = 'Exception:' + str(e)
write_context(self, manage, 0, 0, all_text) write_context(self, manage, 0, 0, all_text)

View File

@ -337,13 +337,15 @@ class WorkflowManage:
answer_text = '\n\n'.join( answer_text = '\n\n'.join(
'\n\n'.join([a.get('content') for a in answer]) for answer in '\n\n'.join([a.get('content') for a in answer]) for answer in
answer_text_list) answer_text_list)
answer_list = reduce(lambda pre, _n: [*pre, *_n], answer_text_list, [])
self.work_flow_post_handler.handler(self.params['chat_id'], self.params['chat_record_id'], self.work_flow_post_handler.handler(self.params['chat_id'], self.params['chat_record_id'],
answer_text, answer_text,
self) self)
return self.base_to_response.to_block_response(self.params['chat_id'], return self.base_to_response.to_block_response(self.params['chat_id'],
self.params['chat_record_id'], answer_text, True self.params['chat_record_id'], answer_text, True
, message_tokens, answer_tokens, , message_tokens, answer_tokens,
_status=status.HTTP_200_OK if self.status == 200 else status.HTTP_500_INTERNAL_SERVER_ERROR) _status=status.HTTP_200_OK if self.status == 200 else status.HTTP_500_INTERNAL_SERVER_ERROR,
other_params={'answer_list': answer_list})
def run_stream(self, current_node, node_result_future, language='zh'): def run_stream(self, current_node, node_result_future, language='zh'):
""" """

View File

@ -13,7 +13,7 @@ from django.core import signing
from django.core.cache import cache from django.core.cache import cache
# alg使用的算法 # alg使用的算法
HEADER = {'typ': 'JWP', 'alg': 'default'} HEADER = {'type': 'JWP', 'alg': 'default'}
TOKEN_KEY = 'solomon_world_token' TOKEN_KEY = 'solomon_world_token'
TOKEN_SALT = 'solomonwanc@gmail.com' TOKEN_SALT = 'solomonwanc@gmail.com'
TIME_OUT = 30 * 60 TIME_OUT = 30 * 60

View File

@ -249,7 +249,7 @@ class FunctionLibSerializer(serializers.Serializer):
mk_instance = FlibInstance(application_dict, 'v1') mk_instance = FlibInstance(application_dict, 'v1')
application_pickle = pickle.dumps(mk_instance) application_pickle = pickle.dumps(mk_instance)
response = HttpResponse(content_type='text/plain', content=application_pickle) response = HttpResponse(content_type='text/plain', content=application_pickle)
response['Content-Disposition'] = f'attachment; filename="{function_lib.name}.flib"' response['Content-Disposition'] = f'attachment; filename="{function_lib.name}.fx"'
return response return response
except Exception as e: except Exception as e:
return result.error(str(e), response_status=status.HTTP_500_INTERNAL_SERVER_ERROR) return result.error(str(e), response_status=status.HTTP_500_INTERNAL_SERVER_ERROR)

View File

@ -105,7 +105,7 @@ const exportFunctionLib = (
loading?: Ref<boolean> loading?: Ref<boolean>
) => { ) => {
return exportFile( return exportFile(
name + '.flib', name + '.fx',
`${prefix}/${id}/export`, `${prefix}/${id}/export`,
undefined, undefined,
loading loading

View File

@ -1,16 +1,50 @@
<template> <template>
<Codemirror <div class="codemirror-editor w-full">
v-bind="$attrs" <Codemirror
ref="cmRef" v-model="data"
:extensions="extensions" ref="cmRef"
:style="codemirrorStyle" :extensions="extensions"
:tab-size="4" :style="codemirrorStyle"
:autofocus="true" :tab-size="4"
/> :autofocus="true"
v-bind="$attrs"
/>
<div class="codemirror-editor__footer">
<el-button text type="info" @click="openCodemirrorDialog" class="magnify">
<AppIcon iconName="app-magnify" style="font-size: 16px"></AppIcon>
</el-button>
</div>
<!-- Codemirror 弹出层 -->
<el-dialog
v-model="dialogVisible"
:title="$t('views.functionLib.functionForm.form.param.code')"
append-to-body
fullscreen
>
<Codemirror
v-model="cloneContent"
:extensions="extensions"
:style="codemirrorStyle"
:tab-size="4"
:autofocus="true"
style="
height: calc(100vh - 160px) !important;
border: 1px solid #bbbfc4;
border-radius: 4px;
"
/>
<template #footer>
<div class="dialog-footer mt-24">
<el-button type="primary" @click="submitDialog"> {{ $t('common.confirm') }}</el-button>
</div>
</template>
</el-dialog>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref, computed, watch } from 'vue'
import { Codemirror } from 'vue-codemirror' import { Codemirror } from 'vue-codemirror'
import { python } from '@codemirror/lang-python' import { python } from '@codemirror/lang-python'
import { oneDark } from '@codemirror/theme-one-dark' import { oneDark } from '@codemirror/theme-one-dark'
@ -19,6 +53,19 @@ import FunctionApi from '@/api/function-lib'
defineOptions({ name: 'CodemirrorEditor' }) defineOptions({ name: 'CodemirrorEditor' })
const props = defineProps<{
modelValue: any
}>()
const emit = defineEmits(['update:modelValue', 'submitDialog'])
const data = computed({
set: (value) => {
emit('update:modelValue', value)
},
get: () => {
return props.modelValue
}
})
function getRangeFromLineAndColumn(state: any, line: number, column: number, end_column?: number) { function getRangeFromLineAndColumn(state: any, line: number, column: number, end_column?: number) {
const l = state.doc.line(line) const l = state.doc.line(line)
const form = l.from + column const form = l.from + column
@ -52,9 +99,39 @@ const regexpLinter = linter(async (view) => {
}) })
const extensions = [python(), regexpLinter, oneDark] const extensions = [python(), regexpLinter, oneDark]
const codemirrorStyle = { const codemirrorStyle = {
height: '210px!important' height: '210px!important',
width: '100%'
} }
const cmRef = ref<InstanceType<typeof Codemirror>>() const cmRef = ref<InstanceType<typeof Codemirror>>()
//
const dialogVisible = ref<boolean>(false)
const cloneContent = ref<string>('')
watch(dialogVisible, (bool) => {
if (!bool) {
emit('submitDialog', cloneContent.value)
}
})
const openCodemirrorDialog = () => {
cloneContent.value = props.modelValue
dialogVisible.value = true
}
function submitDialog() {
emit('submitDialog', cloneContent.value)
dialogVisible.value = false
}
</script> </script>
<style lang="scss"></style> <style lang="scss" scoped>
.codemirror-editor {
position: relative;
&__footer {
position: absolute;
bottom: 10px;
right: 10px;
}
}
</style>

View File

@ -440,7 +440,7 @@
<SyncWebDialog ref="SyncWebDialogRef" @refresh="refresh" /> <SyncWebDialog ref="SyncWebDialogRef" @refresh="refresh" />
<!-- 选择知识库 --> <!-- 选择知识库 -->
<SelectDatasetDialog ref="SelectDatasetDialogRef" @refresh="refreshMigrate" /> <SelectDatasetDialog ref="SelectDatasetDialogRef" @refresh="refreshMigrate" />
<GenerateRelatedDialog ref="GenerateRelatedDialogRef" @refresh="refresh" /> <GenerateRelatedDialog ref="GenerateRelatedDialogRef" @refresh="getList" />
</div> </div>
<div class="mul-operation w-full flex" v-if="multipleSelection.length !== 0"> <div class="mul-operation w-full flex" v-if="multipleSelection.length !== 0">
<el-button :disabled="multipleSelection.length === 0" @click="cancelTaskHandle(1)"> <el-button :disabled="multipleSelection.length === 0" @click="cancelTaskHandle(1)">

View File

@ -133,13 +133,8 @@
</el-text> </el-text>
</h4> </h4>
<div class="function-CodemirrorEditor mb-8" v-if="showEditor"> <div class="mb-8" v-if="showEditor">
<CodemirrorEditor v-model="form.code" /> <CodemirrorEditor v-model="form.code" @submitDialog="submitCodemirrorEditor" />
<div class="function-CodemirrorEditor__footer">
<el-button text type="info" @click="openCodemirrorDialog" class="magnify">
<AppIcon iconName="app-magnify" style="font-size: 16px"></AppIcon>
</el-button>
</div>
</div> </div>
<h4 class="title-decoration-1 mb-16 mt-16"> <h4 class="title-decoration-1 mb-16 mt-16">
{{ $t('common.param.outputParam') }} {{ $t('common.param.outputParam') }}
@ -162,27 +157,6 @@
</div> </div>
</template> </template>
<!-- Codemirror 弹出层 -->
<el-dialog
v-model="dialogVisible"
:title="$t('views.functionLib.functionForm.form.param.code')"
append-to-body
fullscreen
>
<CodemirrorEditor
v-model="cloneContent"
style="
height: calc(100vh - 160px) !important;
border: 1px solid #bbbfc4;
border-radius: 4px;
"
/>
<template #footer>
<div class="dialog-footer mt-24">
<el-button type="primary" @click="submitDialog"> {{ $t('common.confirm') }}</el-button>
</div>
</template>
</el-dialog>
<FunctionDebugDrawer ref="FunctionDebugDrawerRef" /> <FunctionDebugDrawer ref="FunctionDebugDrawerRef" />
<FieldFormDialog ref="FieldFormDialogRef" @refresh="refreshFieldList" /> <FieldFormDialog ref="FieldFormDialogRef" @refresh="refreshFieldList" />
</el-drawer> </el-drawer>
@ -223,9 +197,6 @@ const form = ref<functionLibData>({
permission_type: 'PRIVATE' permission_type: 'PRIVATE'
}) })
const dialogVisible = ref(false)
const cloneContent = ref<any>('')
watch(visible, (bool) => { watch(visible, (bool) => {
if (!bool) { if (!bool) {
isEdit.value = false isEdit.value = false
@ -259,14 +230,8 @@ const rules = reactive({
] ]
}) })
function openCodemirrorDialog() { function submitCodemirrorEditor(val: string) {
cloneContent.value = form.value.code form.value.code = val
dialogVisible.value = true
}
function submitDialog() {
form.value.code = cloneContent.value
dialogVisible.value = false
} }
function close() { function close() {
@ -353,13 +318,4 @@ defineExpose({
open open
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped></style>
.function-CodemirrorEditor__footer {
position: absolute;
bottom: 10px;
right: 10px;
}
.function-CodemirrorEditor {
position: relative;
}
</style>

View File

@ -1,5 +1,5 @@
<template> <template>
<AppAvatar shape="square"> <AppAvatar shape="square" class="avatar-blue">
<img src="@/assets/icon_assigner.svg" style="width: 65%" alt="" /> <img src="@/assets/icon_assigner.svg" style="width: 65%" alt="" />
</AppAvatar> </AppAvatar>
</template> </template>

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="flex-between mb-16"> <div class="flex-between mb-16">
<h5 class="lighter">{{ $t('chat.userInput') }}</h5> <h5 class="lighter">{{ inputFieldConfig.title }}</h5>
<div> <div>
<el-button type="primary" link @click="openChangeTitleDialog"> <el-button type="primary" link @click="openChangeTitleDialog">
<el-icon> <el-icon>

View File

@ -23,7 +23,8 @@
:prop="'input_field_list.' + index + '.value'" :prop="'input_field_list.' + index + '.value'"
:rules="{ :rules="{
required: item.is_required, required: item.is_required,
message: item.source === 'reference' message:
item.source === 'reference'
? $t('views.functionLib.functionForm.form.param.selectPlaceholder') ? $t('views.functionLib.functionForm.form.param.selectPlaceholder')
: $t('views.functionLib.functionForm.form.param.inputPlaceholder'), : $t('views.functionLib.functionForm.form.param.inputPlaceholder'),
trigger: 'blur' trigger: 'blur'
@ -76,17 +77,13 @@
<h5 class="lighter mb-8"> <h5 class="lighter mb-8">
{{ $t('views.functionLib.functionForm.form.param.code') }} {{ $t('views.functionLib.functionForm.form.param.code') }}
</h5> </h5>
<div class="function-CodemirrorEditor mb-8" v-if="showEditor"> <div class="mb-8" v-if="showEditor">
<CodemirrorEditor <CodemirrorEditor
v-model="chat_data.code" v-model="chat_data.code"
@wheel="wheel" @wheel="wheel"
style="height: 130px !important" style="height: 130px !important"
@submitDialog="submitCodemirrorEditor"
/> />
<div class="function-CodemirrorEditor__footer">
<el-button text type="info" @click="openCodemirrorDialog" class="magnify">
<AppIcon iconName="app-magnify" style="font-size: 16px"></AppIcon>
</el-button>
</div>
</div> </div>
<el-form-item <el-form-item
@ -113,27 +110,6 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
<FieldFormDialog ref="FieldFormDialogRef" @refresh="refreshFieldList" /> <FieldFormDialog ref="FieldFormDialogRef" @refresh="refreshFieldList" />
<!-- Codemirror 弹出层 -->
<el-dialog
v-model="dialogVisible"
:title="$t('views.functionLib.functionForm.form.param.code')"
append-to-body
fullscreen
>
<CodemirrorEditor
v-model="cloneContent"
style="
height: calc(100vh - 160px) !important;
border: 1px solid #bbbfc4;
border-radius: 4px;
"
/>
<template #footer>
<div class="dialog-footer mt-24">
<el-button type="primary" @click="submitDialog"> {{ $t('common.confirm') }}</el-button>
</div>
</template>
</el-dialog>
</NodeContainer> </NodeContainer>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -190,17 +166,8 @@ const validate = () => {
}) })
} }
const dialogVisible = ref(false) function submitCodemirrorEditor(val: string) {
const cloneContent = ref('') set(props.nodeModel.properties.node_data, 'code', val)
function openCodemirrorDialog() {
cloneContent.value = chat_data.value.code
dialogVisible.value = true
}
function submitDialog() {
set(props.nodeModel.properties.node_data, 'code', cloneContent.value)
dialogVisible.value = false
} }
function openAddDialog(data?: any, index?: any) { function openAddDialog(data?: any, index?: any) {
@ -244,13 +211,4 @@ onMounted(() => {
}, 100) }, 100)
}) })
</script> </script>
<style lang="scss"> <style lang="scss"></style>
.function-CodemirrorEditor__footer {
position: absolute;
bottom: 10px;
right: 10px;
}
.function-CodemirrorEditor {
position: relative;
}
</style>

View File

@ -7,6 +7,7 @@
require-asterisk-position="right" require-asterisk-position="right"
label-width="auto" label-width="auto"
ref="replyNodeFormRef" ref="replyNodeFormRef"
hide-required-asterisk
> >
<template v-for="(item, index) in form_data.variable_list" :key="item.id"> <template v-for="(item, index) in form_data.variable_list" :key="item.id">
<el-card shadow="never" class="card-never mb-8" style="--el-card-padding: 12px"> <el-card shadow="never" class="card-never mb-8" style="--el-card-padding: 12px">
@ -17,7 +18,11 @@
{{ $t('views.applicationWorkflow.variable.label') }} {{ $t('views.applicationWorkflow.variable.label') }}
<span class="danger">*</span> <span class="danger">*</span>
</div> </div>
<el-button text @click="deleteVariable(index)" v-if="index !== 0"> <el-button
text
@click="deleteVariable(index)"
v-if="form_data.variable_list.length > 1"
>
<el-icon> <el-icon>
<Delete /> <Delete />
</el-icon> </el-icon>
@ -34,116 +39,102 @@
@change="variableChange(item)" @change="variableChange(item)"
/> />
</el-form-item> </el-form-item>
<el-form-item> <div class="flex-between mb-8">
<template #label> <span class="lighter"
<div class="flex-between"> >{{ $t('views.applicationWorkflow.nodes.variableAssignNode.assign')
<div> }}<span class="danger">*</span></span
<span >
>{{ $t('views.applicationWorkflow.nodes.variableAssignNode.assign') <el-select :teleported="false" v-model="item.source" size="small" style="width: 85px">
}}<span class="danger">*</span></span <el-option
> :label="$t('views.applicationWorkflow.nodes.replyNode.replyContent.reference')"
</div> value="referencing"
<el-select />
:teleported="false" <el-option
v-model="item.source" :label="$t('views.applicationWorkflow.nodes.replyNode.replyContent.custom')"
size="small" value="custom"
style="width: 85px" />
> </el-select>
<el-option </div>
:label="$t('views.applicationWorkflow.nodes.replyNode.replyContent.reference')"
value="referencing" <div v-if="item.source === 'custom'" class="flex w-full">
/> <el-select
<el-option v-model="item.type"
:label="$t('views.applicationWorkflow.nodes.replyNode.replyContent.custom')" style="max-width: 85px"
value="custom" class="mr-8"
/> @change="form_data.variable_list[index].value = null"
</el-select> >
</div> <el-option v-for="item in typeOptions" :key="item" :label="item" :value="item" />
</template> </el-select>
<div v-if="item.source === 'custom'" class="flex">
<el-row :gutter="8"> <el-form-item
<el-col :span="8"> v-if="item.type === 'string'"
<el-select v-model="item.type" style="width: 85px"> :prop="'variable_list.' + index + '.value'"
<el-option :rules="{
v-for="item in typeOptions" message: t('common.inputPlaceholder'),
:key="item" trigger: 'blur',
:label="item" required: true
:value="item" }"
/> >
</el-select> <el-input
</el-col> v-model="item.value"
<el-col :span="16"> :placeholder="$t('common.inputPlaceholder')"
<el-form-item show-word-limit
v-if="item.type === 'string'" clearable
:prop="'variable_list.' + index + '.value'" @wheel="wheel"
:rules="{ ></el-input>
message: t('dynamicsForm.tip.requiredMessage'), </el-form-item>
trigger: 'blur', <el-form-item
required: true v-else-if="item.type === 'num'"
}" :prop="'variable_list.' + index + '.value'"
> :rules="{
<el-input message: $t('common.inputPlaceholder'),
class="ml-4" trigger: 'blur',
v-model="item.value" required: true
:placeholder="$t('common.inputPlaceholder')" }"
show-word-limit >
clearable <el-input-number v-model="item.value"></el-input-number>
@wheel="wheel" </el-form-item>
></el-input> <el-form-item
</el-form-item>
<el-form-item
v-else-if="item.type === 'num'"
:prop="'variable_list.' + index + '.value'"
:rules="{
message: $t('common.inputPlaceholder'),
trigger: 'blur',
required: true
}"
>
<el-input-number class="ml-4" v-model="item.value"></el-input-number>
</el-form-item>
<el-form-item
v-else-if="item.type === 'json'"
:prop="'variable_list.' + index + '.value'"
:rules="[
{
message: $t('common.inputPlaceholder'),
trigger: 'blur',
required: true
},
{
validator: (rule: any, value: any, callback: any) => {
try {
JSON.parse(value)
callback() // Valid JSON
} catch (e) {
callback(new Error('Invalid JSON format'))
}
},
trigger: 'blur'
}
]"
>
<el-input
class="ml-4"
v-model="item.value"
:placeholder="$t('common.inputPlaceholder')"
type="textarea"
autosize
></el-input>
</el-form-item>
</el-col>
</el-row>
</div>
<NodeCascader
v-else
ref="nodeCascaderRef2"
:nodeModel="nodeModel"
class="w-full" class="w-full"
:placeholder="$t('views.applicationWorkflow.variable.placeholder')" v-else-if="item.type === 'json'"
v-model="item.reference" :prop="'variable_list.' + index + '.value'"
/> :rules="[
</el-form-item> {
message: $t('common.inputPlaceholder'),
trigger: 'blur',
required: true
},
{
validator: (rule: any, value: any, callback: any) => {
try {
JSON.parse(value)
callback() // Valid JSON
} catch (e) {
callback(new Error('Invalid JSON format'))
}
},
trigger: 'blur'
}
]"
>
<CodemirrorEditor
v-model="item.value"
:style="{
height: '100px'
}"
@submitDialog="(val: string) => (form_data.variable_list[index].value = val)"
/>
</el-form-item>
</div>
<NodeCascader
v-else
ref="nodeCascaderRef2"
:nodeModel="nodeModel"
class="w-full"
:placeholder="$t('views.applicationWorkflow.variable.placeholder')"
v-model="item.reference"
/>
</el-card> </el-card>
</template> </template>
<el-button link type="primary" @click="addVariable"> <el-button link type="primary" @click="addVariable">
@ -212,6 +203,7 @@ function submitDialog(val: string) {
const replyNodeFormRef = ref() const replyNodeFormRef = ref()
const nodeCascaderRef = ref() const nodeCascaderRef = ref()
const nodeCascaderRef2 = ref() const nodeCascaderRef2 = ref()
const validate = async () => { const validate = async () => {
// console.log(replyNodeFormRef.value.validate()) // console.log(replyNodeFormRef.value.validate())
let ps = [ let ps = [