Skip to content
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 6.1 on 2026-09-16 08:54

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("application", "0018_application_default_model_setting_and_more"),
]

operations = [
migrations.AddField(
model_name="applicationversion",
name="publish_desc",
field=models.CharField(default="", max_length=1024, verbose_name="更新说明"),
),
]
1 change: 1 addition & 0 deletions apps/application/models/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ class ApplicationVersion(AppModelMixin):
name = models.CharField(verbose_name="版本名称", max_length=128, default="")
publish_user_id = models.UUIDField(verbose_name="发布者id", max_length=128, default=None, null=True)
publish_user_name = models.CharField(verbose_name="发布者名称", max_length=128, default="")
publish_desc = models.CharField(verbose_name="更新说明", max_length=1024, default="")
workspace_id = models.CharField(max_length=64, verbose_name="工作空间id", default="default", db_index=True)
application_name = models.CharField(max_length=128, verbose_name="应用名称")
desc = models.CharField(max_length=512, verbose_name="引用描述", default="")
Expand Down
17 changes: 14 additions & 3 deletions apps/application/serializers/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

import requests
import uuid_utils.compat as uuid
from application.workflow.common import Workflow
from application.workflow.common import new_instance
from application.long_term_memory import schedule_extract_long_term_memory
from application.models.application import Application, ApplicationFolder, ApplicationTypeChoices, ApplicationVersion
from application.models.application_access_token import ApplicationAccessToken
Expand Down Expand Up @@ -1107,6 +1107,12 @@ class ApplicationOperateSerializer(serializers.Serializer):
application_id = serializers.UUIDField(required=True, label=_("Application ID"))
user_id = serializers.UUIDField(required=True, label=_("User ID"))
workspace_id = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("Workspace ID"))
publish_name = serializers.CharField(
required=False, max_length=128, allow_null=True, allow_blank=True, label=_("Publish Name")
)
publish_desc = serializers.CharField(
required=False, max_length=1024, allow_null=True, allow_blank=True, label=_("Publish Desc")
)

def is_valid(self, *, raise_exception=False):
super().is_valid(raise_exception=True)
Expand Down Expand Up @@ -1260,6 +1266,10 @@ def publish(self, instance, with_valid=True):
self.is_valid()
user_id = self.data.get("user_id")
workspace_id = self.data.get("workspace_id")
name = (instance or {}).get("publish_name")
if not name or not str(name).strip():
raise AppApiException(500, _("publish_name is required"))
publish_desc = (instance or {}).get("publish_desc") or ""
user = QuerySet(User).filter(id=user_id).first()
application = (
QuerySet(Application).filter(id=self.data.get("application_id"), workspace_id=workspace_id).first()
Expand All @@ -1268,7 +1278,7 @@ def publish(self, instance, with_valid=True):
work_flow = application.work_flow
if work_flow is None:
raise AppApiException(500, _("work_flow is a required field"))
Workflow.new_instance(work_flow).is_valid()
new_instance(work_flow).is_valid()
base_node = get_base_node_work_flow(work_flow)
if base_node is not None:
node_data = base_node.get("properties").get("node_data")
Expand All @@ -1283,9 +1293,10 @@ def publish(self, instance, with_valid=True):
work_flow_version = ApplicationVersion(
work_flow=application.work_flow,
application=application,
name=timezone.localtime(timezone.now()).strftime("%Y-%m-%d %H:%M:%S"),
name=name,
publish_user_id=user_id,
publish_user_name=user.username,
publish_desc=publish_desc,
workspace_id=workspace_id,
)
self.reset_application_version(work_flow_version, application)
Expand Down
90 changes: 56 additions & 34 deletions apps/application/serializers/application_version.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# coding=utf-8
"""
@project: MaxKB
@Author:虎虎
@file: application_version.py
@date:2025/6/3 16:25
@desc:
@project: MaxKB
@Author:虎虎
@file: application_version.py
@date:2025/6/3 16:25
@desc:
"""

from typing import Dict

from django.db.models import QuerySet
Expand All @@ -19,21 +20,33 @@

class ApplicationVersionQuerySerializer(serializers.Serializer):
application_id = serializers.UUIDField(required=True, label=_("Application ID"))
name = serializers.CharField(required=False, allow_null=True, allow_blank=True,
label=_("summary"))
name = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("summary"))


class ApplicationVersionModelSerializer(serializers.ModelSerializer):
class Meta:
model = ApplicationVersion
fields = ['id', 'name', 'workspace_id', 'application_id', 'work_flow', 'publish_user_id', 'publish_user_name',
'create_time',
'update_time']
fields = [
"id",
"name",
"publish_desc",
"workspace_id",
"application_id",
"work_flow",
"publish_user_id",
"publish_user_name",
"create_time",
"update_time",
]


class ApplicationVersionEditSerializer(serializers.Serializer):
name = serializers.CharField(required=False, max_length=128, allow_null=True, allow_blank=True,
label=_("Version Name"))
name = serializers.CharField(
required=False, max_length=128, allow_null=True, allow_blank=True, label=_("Version Name")
)
publish_desc = serializers.CharField(
required=False, max_length=1024, allow_null=True, allow_blank=True, label=_("Publish Description")
)


class ApplicationVersionSerializer(serializers.Serializer):
Expand All @@ -43,11 +56,11 @@ class Query(serializers.Serializer):
workspace_id = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("Workspace ID"))

def get_query_set(self, query):
query_set = QuerySet(ApplicationVersion).filter(application_id=query.get('application_id'))
if 'name' in query and query.get('name') is not None:
query_set = query_set.filter(name__contains=query.get('name'))
if 'workspace_id' in self.data and self.data.get('workspace_id') is not None:
query_set = query_set.filter(workspace_id=self.data.get('workspace_id'))
query_set = QuerySet(ApplicationVersion).filter(application_id=query.get("application_id"))
if "name" in query and query.get("name") is not None:
query_set = query_set.filter(name__contains=query.get("name"))
if "workspace_id" in self.data and self.data.get("workspace_id") is not None:
query_set = query_set.filter(workspace_id=self.data.get("workspace_id"))
return query_set.order_by("-create_time")

def list(self, query, with_valid=True):
Expand All @@ -60,48 +73,57 @@ def list(self, query, with_valid=True):
def page(self, query, current_page, page_size, with_valid=True):
if with_valid:
self.is_valid(raise_exception=True)
return page_search(current_page, page_size,
self.get_query_set(query),
post_records_handler=lambda v: ApplicationVersionModelSerializer(v).data)
return page_search(
current_page,
page_size,
self.get_query_set(query),
post_records_handler=lambda v: ApplicationVersionModelSerializer(v).data,
)

class Operate(serializers.Serializer):
workspace_id = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("Workspace ID"))
application_id = serializers.UUIDField(required=True, label=_("Application ID"))
application_version_id = serializers.UUIDField(required=True,
label=_("Application version ID"))
application_version_id = serializers.UUIDField(required=True, label=_("Application version ID"))

def is_valid(self, *, raise_exception=False):
super().is_valid(raise_exception=True)
workspace_id = self.data.get('workspace_id')
query_set = QuerySet(Application).filter(id=self.data.get('application_id'))
workspace_id = self.data.get("workspace_id")
query_set = QuerySet(Application).filter(id=self.data.get("application_id"))
if workspace_id:
query_set = query_set.filter(workspace_id=workspace_id)
if not query_set.exists():
raise AppApiException(500, _('Application id does not exist'))
raise AppApiException(500, _("Application id does not exist"))

def one(self, with_valid=True):
if with_valid:
self.is_valid(raise_exception=True)
application_version = QuerySet(ApplicationVersion).filter(application_id=self.data.get('application_id'),
id=self.data.get(
'application_version_id')).first()
application_version = (
QuerySet(ApplicationVersion)
.filter(application_id=self.data.get("application_id"), id=self.data.get("application_version_id"))
.first()
)
if application_version is not None:
return ApplicationVersionModelSerializer(application_version).data
else:
raise AppApiException(500, _('Workflow version does not exist'))
raise AppApiException(500, _("Workflow version does not exist"))

def edit(self, instance: Dict, with_valid=True):
if with_valid:
self.is_valid(raise_exception=True)
ApplicationVersionEditSerializer(data=instance).is_valid(raise_exception=True)
application_version = QuerySet(ApplicationVersion).filter(application_id=self.data.get('application_id'),
id=self.data.get(
'application_version_id')).first()
application_version = (
QuerySet(ApplicationVersion)
.filter(application_id=self.data.get("application_id"), id=self.data.get("application_version_id"))
.first()
)
if application_version is not None:
name = instance.get('name', None)
name = instance.get("name", None)
publish_desc = instance.get("publish_desc", None)
if name is not None and len(name) > 0:
application_version.name = name
if publish_desc is not None and len(publish_desc) > 0:
application_version.publish_desc = publish_desc
application_version.save()
return ApplicationVersionModelSerializer(application_version).data
else:
raise AppApiException(500, _('Workflow version does not exist'))
raise AppApiException(500, _("Workflow version does not exist"))
3 changes: 3 additions & 0 deletions apps/application/workflow/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@ def reset_prompt(self, prompt):
prompt = node_field.reset_variable(prompt)
return prompt

def is_valid(self):
pass


class WorkflowType(Enum):
# 应用
Expand Down
7 changes: 5 additions & 2 deletions ui/src/api/admin/workspace/application/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,11 @@ const putBatchMoveApplications = (applicationIds: string[], folderId: string) =>
}

/** 发布工作空间智能体。 */
const putApplicationPublish = (applicationId: string) => {
return put<Record<string, never>, ApplicationDetail>(`${getPrefix()}/${applicationId}/publish`, {})
const putApplicationPublish = (applicationId: string, publishName: string, publishDesc?: string) => {
return put<Record<string, unknown>, ApplicationDetail>(`${getPrefix()}/${applicationId}/publish`, {
publish_name: publishName,
publish_desc: publishDesc,
})
}

/** 使用指定模型流式生成或优化系统提示词。 */
Expand Down
4 changes: 2 additions & 2 deletions ui/src/api/types/workflow-version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export interface WorkflowVersion {
id: string
name: string
/** 更新说明;旧版本接口可能不返回。 */
description?: string | null
publish_desc?: string | null
work_flow: LogicFlow.GraphConfigData
publish_user_name: string
create_time: string
Expand All @@ -15,5 +15,5 @@ export interface WorkflowVersion {
/** 发布历史的标题和更新说明编辑内容。 */
export interface WorkflowVersionPayload {
name: string
description: string
publish_desc: string
}
25 changes: 13 additions & 12 deletions ui/src/views/workflow/application/ApplicationWorkflowView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import WorkflowViewLayout from '../components/WorkflowViewLayout.vue'
import ButtonPublishHistory from './ButtonPublishHistory.vue'
import TemplateStoreDialog from '@/views/application/template-store/TemplateStoreDialog.vue'
import DebugPanel from './debug/DebugPanel.vue'
import ApplicationPublishDialog from './components/ApplicationPublishDialog.vue'
import { getResourceScope } from '@/utils/resource-context.ts'
import { goBack } from './navigation'

Expand Down Expand Up @@ -211,23 +212,21 @@ function handleSaveDefaultModelSetting(settings: DefaultModelSettingPayload) {
return handleSave()
}

/* 发布工作流 */
/* 发布工作流:先校验画布,再由弹窗收集发布标题与更新说明 */
const publishDialogRef = useTemplateRef<InstanceType<typeof ApplicationPublishDialog>>('publishDialogRef')

function handlePublish() {
workflowRef.value?.validate().then(() => {
publishing.value = true
return saveApplication(undefined, false)
.then(() => ApplicationApi.putApplicationPublish(applicationId))
.then((application) => {
applicationDetail.value = application
saveTime.value = application.update_time || saveTime.value
MsgSuccess('发布成功')
})
.finally(() => {
publishing.value = false
})
publishDialogRef.value?.open()
})
}

/* 发布成功:更新详情与保存时间,保持与保存流程一致 */
function handlePublished(application: ApplicationDetail) {
applicationDetail.value = application
saveTime.value = application.update_time || saveTime.value
}

/* 退出工作流 */
function handleBack() {
if (loading.value || saving.value || confirmingExit.value) return
Expand Down Expand Up @@ -382,5 +381,7 @@ onBeforeUnmount(() => stopAutoSave())
<DebugPanel ref="debugPanelRef" />
<!-- 模版中心 -->
<TemplateStoreDialog ref="templateStoreDialogRef" source="work_flow" :applying="saving" @use="handleUseTemplate" />
<!-- 发布弹窗 -->
<ApplicationPublishDialog ref="publishDialogRef" :resource-id="applicationId" @published="handlePublished" />
</WorkflowViewLayout>
</template>
Loading
Loading