From c506a23157695c47346b7bb37610e2ade8f33886 Mon Sep 17 00:00:00 2001 From: CaptainB Date: Tue, 23 Dec 2025 14:32:18 +0800 Subject: [PATCH] feat: add StoreKnowledge API to fetch Appstore tools and update routing --- apps/knowledge/serializers/knowledge.py | 56 +++++++++++++++++++++++-- apps/knowledge/urls.py | 1 + apps/knowledge/views/knowledge.py | 18 ++++++++ apps/tools/serializers/tool.py | 2 + 4 files changed, 74 insertions(+), 3 deletions(-) diff --git a/apps/knowledge/serializers/knowledge.py b/apps/knowledge/serializers/knowledge.py index cbaffd3fb..070676822 100644 --- a/apps/knowledge/serializers/knowledge.py +++ b/apps/knowledge/serializers/knowledge.py @@ -2,12 +2,15 @@ import io import json import os import re +import tempfile import traceback +import zipfile from collections import defaultdict from functools import reduce from tempfile import TemporaryDirectory from typing import Dict, List +import requests import uuid_utils.compat as uuid from celery_once import AlreadyQueued from django.core import validators @@ -19,7 +22,6 @@ from django.http import HttpResponse from django.utils.translation import gettext_lazy as _ from rest_framework import serializers -from application.flow.tools import get_workflow_resource, get_node_handle_callback, save_workflow_mapping from application.models import ApplicationKnowledgeMapping from common.config.embedding_config import VectorStore from common.database_model_manage.database_model_manage import DatabaseModelManage @@ -32,7 +34,7 @@ from common.utils.fork import Fork, ChildLink from common.utils.logger import maxkb_logger from common.utils.split_model import get_split_model from knowledge.models import Knowledge, KnowledgeScope, KnowledgeType, Document, Paragraph, Problem, \ - ProblemParagraphMapping, TaskType, State, SearchMode, KnowledgeFolder, File, Tag, KnowledgeWorkflow + ProblemParagraphMapping, TaskType, State, SearchMode, KnowledgeFolder, File, Tag from knowledge.serializers.common import ProblemParagraphManage, drop_knowledge_index, \ get_embedding_model_id_by_knowledge_id, MetaSerializer, \ GenerateRelatedSerializer, get_embedding_model_by_knowledge_id, list_paragraph, write_image, zip_dir, \ @@ -44,7 +46,6 @@ from knowledge.task.sync import sync_web_knowledge, sync_replace_web_knowledge from maxkb.conf import PROJECT_DIR from models_provider.models import Model from system_manage.models import WorkspaceUserResourcePermission, AuthTargetType -from system_manage.models.resource_mapping import ResourceType, ResourceMapping from system_manage.serializers.user_resource_permission import UserResourcePermissionSerializer from users.serializers.user import is_workspace_manage @@ -768,6 +769,55 @@ class KnowledgeSerializer(serializers.Serializer): } for p in p_list ] + class StoreKnowledge(serializers.Serializer): + user_id = serializers.UUIDField(required=True, label=_("User ID")) + name = serializers.CharField(required=False, label=_("tool name"), allow_null=True, allow_blank=True) + + def get_appstore_templates(self): + self.is_valid(raise_exception=True) + # 下载zip文件 + try: + res = requests.get('https://apps-assets.fit2cloud.com/stable/maxkb.json.zip', timeout=5) + res.raise_for_status() + # 创建临时文件保存zip + with tempfile.NamedTemporaryFile(delete=False, suffix='.zip') as temp_zip: + temp_zip.write(res.content) + temp_zip_path = temp_zip.name + + try: + # 解压zip文件 + with zipfile.ZipFile(temp_zip_path, 'r') as zip_ref: + # 获取zip中的第一个文件(假设只有一个json文件) + json_filename = zip_ref.namelist()[0] + json_content = zip_ref.read(json_filename) + + # 将json转换为字典 + tool_store = json.loads(json_content.decode('utf-8')) + tag_dict = {tag['name']: tag['key'] for tag in tool_store['additionalProperties']['tags']} + filter_apps = [] + for tool in tool_store['apps']: + if self.data.get('name', '') != '': + if self.data.get('name').lower() not in tool.get('name', '').lower(): + continue + if not tool['downloadUrl'].endswith('.kbwf'): + continue + versions = tool.get('versions', []) + tool['label'] = tag_dict[tool.get('tags')[0]] if tool.get('tags') else '' + tool['version'] = next( + (version.get('name') for version in versions if + version.get('downloadUrl') == tool['downloadUrl']), + ) + filter_apps.append(tool) + + tool_store['apps'] = filter_apps + return tool_store + finally: + # 清理临时文件 + os.unlink(temp_zip_path) + except Exception as e: + maxkb_logger.error(f"fetch appstore tools error: {e}") + return {'apps': [], 'additionalProperties': {'tags': []}} + class Tags(serializers.Serializer): workspace_id = serializers.CharField(required=True, label=_('workspace id')) user_id = serializers.UUIDField(required=True, label=_('user id')) diff --git a/apps/knowledge/urls.py b/apps/knowledge/urls.py index 6b345fae8..7250604fc 100644 --- a/apps/knowledge/urls.py +++ b/apps/knowledge/urls.py @@ -7,6 +7,7 @@ app_name = "knowledge" urlpatterns = [ path('workspace/knowledge/document/template/export', views.Template.as_view()), path('workspace/knowledge/document/table_template/export', views.TableTemplate.as_view()), + path('workspace/store/knowledge_template', views.KnowledgeView.StoreKnowledge.as_view()), path('workspace//knowledge', views.KnowledgeView.as_view()), path('workspace//knowledge/base', views.KnowledgeBaseView.as_view()), path('workspace//knowledge/workflow', views.KnowledgeWorkflowView.as_view()), diff --git a/apps/knowledge/views/knowledge.py b/apps/knowledge/views/knowledge.py index 5e960b67c..e08ac0110 100644 --- a/apps/knowledge/views/knowledge.py +++ b/apps/knowledge/views/knowledge.py @@ -15,6 +15,7 @@ from knowledge.models import KnowledgeScope from knowledge.serializers.common import get_knowledge_operation_object from knowledge.serializers.knowledge import KnowledgeSerializer from models_provider.serializers.model_serializer import ModelSerializer +from tools.api.tool import GetInternalToolAPI class KnowledgeView(APIView): @@ -220,6 +221,23 @@ class KnowledgeView(APIView): } ).hit_test()) + class StoreKnowledge(APIView): + authentication_classes = [TokenAuth] + + @extend_schema( + methods=['GET'], + description=_("Get Appstore tools"), + summary=_("Get Appstore tools"), + operation_id=_("Get Appstore tools"), # type: ignore + responses=GetInternalToolAPI.get_response(), + tags=[_("Tool")] # type: ignore + ) + def get(self, request: Request): + return result.success(KnowledgeSerializer.StoreKnowledge(data={ + 'user_id': request.user.id, + 'name': request.query_params.get('name', ''), + }).get_appstore_templates()) + class Embedding(APIView): authentication_classes = [TokenAuth] diff --git a/apps/tools/serializers/tool.py b/apps/tools/serializers/tool.py index bda3acb3b..cac977941 100644 --- a/apps/tools/serializers/tool.py +++ b/apps/tools/serializers/tool.py @@ -761,6 +761,8 @@ class ToolSerializer(serializers.Serializer): if self.data.get('name', '') != '': if self.data.get('name').lower() not in tool.get('name', '').lower(): continue + if not tool['downloadUrl'].endswith('.tool'): + continue versions = tool.get('versions', []) tool['label'] = tag_dict[tool.get('tags')[0]] if tool.get('tags') else '' tool['version'] = next(