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
83 changes: 81 additions & 2 deletions apps/application/flow/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,8 @@ async def _yield_mcp_response(chat_model, message_list, mcp_servers, mcp_output_
tools = await client.get_tools()
agent = create_react_agent(chat_model, tools)
recursion_limit = int(CONFIG.get("LANGCHAIN_GRAPH_RECURSION_LIMIT", '25'))
response = agent.astream({"messages": message_list}, config={"recursion_limit": recursion_limit}, stream_mode='messages')
response = agent.astream({"messages": message_list}, config={"recursion_limit": recursion_limit},
stream_mode='messages')

# 用于存储工具调用信息(按 tool_id)以及按 index 聚合分片
tool_calls_info = {}
Expand Down Expand Up @@ -396,7 +397,6 @@ def get_real_error(exc):
raise RuntimeError(error_msg) from None



def mcp_response_generator(chat_model, message_list, mcp_servers, mcp_output_enable=True):
"""使用全局事件循环,不创建新实例"""
result_queue = queue.Queue()
Expand Down Expand Up @@ -427,3 +427,82 @@ async def _run():

async def anext_async(agen):
return await agen.__anext__()


target_source_node_mapping = {
'TOOL': {'tool-lib-node': lambda n: [n.get('properties').get('node_data').get('tool_lib_id')]},
'MODEL': {'ai-chat-node': lambda n: [n.get('properties').get('node_data').get('model_id')],
'question-node': lambda n: [n.get('properties').get('node_data').get('model_id')],
'speech-to-text-node': lambda n: [n.get('properties').get('node_data').get('stt_model_id')],
'text-to-speech-node': lambda n: [n.get('properties').get('node_data').get('tts_model_id')],
'image-to-video-node': lambda n: [n.get('properties').get('node_data').get('model_id')],
'image-generate-node': lambda n: [n.get('properties').get('node_data').get('model_id')],
'intent-node': lambda n: [n.get('properties').get('node_data').get('model_id')],
'image-understand-node': lambda n: [n.get('properties').get('node_data').get('model_id')],
'parameter-extraction-node': lambda n: [n.get('properties').get('node_data').get('model_id')],
'video-understand-node': lambda n: [n.get('properties').get('node_data').get('model_id')]
},
'KNOWLEDGE': {'search-knowledge-node': lambda n: n.get('properties').get('node_data').get('knowledge_id_list')},
'APPLICATION': {
'application-node': lambda n: [n.get('properties').get('node_data').get('application_id')]
}
}


def get_node_handle_callback(source_type, source_id):
def node_handle_callback(node):
from system_manage.models.resource_mapping import ResourceMapping
response = []
for key, value in target_source_node_mapping.items():
if node.get('type') in value:
call = value.get(node.get('type'))
target_source_id_list = call(node)
for target_source_id in target_source_id_list:
if target_source_id:
response.append(ResourceMapping(source_type=source_type, target_type=key, source_id=source_id,
target_id=target_source_id))
return response

return node_handle_callback


def get_workflow_resource(workflow, node_handle):
response = []
if 'nodes' in workflow:
for node in workflow.get('nodes'):
rs = node_handle(node)
if rs:
for r in rs:
response.append(r)
if node.get('type') == 'loop-node':
r = get_workflow_resource(node.get('properties', {}).get('node_data', {}).get('loop_body'), node_handle)
for rn in r:
response.append(rn)
return list({(str(item.target_type) + str(item.target_id)): item for item in response}.values())
return []


def get_instance_resource(instance, source_type, source_id, target_type, field_call_list):
response = []
from system_manage.models.resource_mapping import ResourceMapping
for field_call in field_call_list:
target_id = field_call(instance)
if target_id:
response.append(ResourceMapping(source_type=source_type, target_type=target_type, source_id=source_id,
target_id=target_id))
return response


def save_workflow_mapping(workflow, source_type, source_id, other_resource_mapping=None):
if not other_resource_mapping:
other_resource_mapping = []
from system_manage.models.resource_mapping import ResourceMapping
from django.db.models import QuerySet
QuerySet(ResourceMapping).filter(source_type=source_type, source_id=source_id).delete()
resource_mapping_list = get_workflow_resource(workflow,
get_node_handle_callback(source_type,
source_id))
if resource_mapping_list:
resource_mapping_list += other_resource_mapping
QuerySet(ResourceMapping).bulk_create(
{(str(item.target_type) + str(item.target_id)): item for item in resource_mapping_list}.values())
2 changes: 2 additions & 0 deletions apps/application/serializers/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from application.models.application import Application, ApplicationTypeChoices, ApplicationKnowledgeMapping, \
ApplicationFolder, ApplicationVersion
from application.models.application_access_token import ApplicationAccessToken
from application.serializers.common import update_resource_mapping_by_application
from common import result
from common.cache_data.application_access_token_cache import del_application_access_token
from common.database_model_manage.database_model_manage import DatabaseModelManage
Expand Down Expand Up @@ -780,6 +781,7 @@ def publish(self, instance, with_valid=True):
application_access_token.save()
else:
access_token = application_access_token.access_token
update_resource_mapping_by_application(self.data.get("application_id"))
del_application_access_token(access_token)
return self.one(with_valid=False)

Expand Down
23 changes: 18 additions & 5 deletions apps/application/serializers/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,18 @@
@date:2025/6/9 13:42
@desc:
"""
import json
from typing import List

from django.core.cache import cache
from django.db.models import QuerySet
from django.utils.translation import gettext_lazy as _
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

from application.chat_pipeline.step.chat_step.i_chat_step import PostResponseHandler
from application.models import Application, ChatRecord, Chat, ApplicationVersion, ChatUserType, ApplicationTypeChoices, \
ApplicationKnowledgeMapping
from application.serializers.application_chat import ChatCountSerializer
from common.constants.cache_version import Cache_Version
from common.database_model_manage.database_model_manage import DatabaseModelManage
from common.encoder.encoder import SystemEncoder
from common.exception.app_exception import ChatException
from knowledge.models import Document
from models_provider.models import Model
Expand Down Expand Up @@ -165,7 +162,7 @@ def to_base_pipeline_manage_params(self):
'mcp_output_enable': self.application.mcp_output_enable,
}

def to_pipeline_manage_params(self, problem_text: str, post_response_handler: PostResponseHandler,
def to_pipeline_manage_params(self, problem_text: str, post_response_handler,
exclude_paragraph_id_list, chat_user_id: str, chat_user_type, stream=True,
form_data=None):
if form_data is None:
Expand Down Expand Up @@ -319,3 +316,19 @@ def get_cache(chat_id):
if chat_info_dict:
return ChatInfo.map_to_chat_info(chat_info_dict)
return None


def update_resource_mapping_by_application(application_id: str):
from application.flow.tools import get_instance_resource, save_workflow_mapping
from system_manage.models.resource_mapping import ResourceType
application = QuerySet(Application).filter(id=application_id).first()
instance_mapping = get_instance_resource(application, ResourceType.APPLICATION, str(application.id),
ResourceType.MODEL,
[lambda i: i.tts_model_id, lambda i: i.stt_model_id, ])
if application.type == 'WORK_FLOW':
save_workflow_mapping(application.work_flow, ResourceType.APPLICATION, str(application_id),
instance_mapping)
return
else:
save_workflow_mapping({}, ResourceType.APPLICATION, str(application_id),
instance_mapping)
25 changes: 24 additions & 1 deletion apps/knowledge/serializers/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,19 @@
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers

from application.flow.tools import save_workflow_mapping
from common.config.embedding_config import ModelManage
from common.db.search import native_search
from common.db.sql_execute import sql_execute, update_execute
from common.exception.app_exception import AppApiException
from common.utils.common import get_file_content
from common.utils.fork import Fork
from common.utils.logger import maxkb_logger
from knowledge.models import Document
from knowledge.models import Document, KnowledgeWorkflow, KnowledgeWorkflowVersion, KnowledgeType
from knowledge.models import Paragraph, Problem, ProblemParagraphMapping, Knowledge, File
from maxkb.conf import PROJECT_DIR
from models_provider.tools import get_model, get_model_default_params
from system_manage.models.resource_mapping import ResourceMapping, ResourceType


class MetaSerializer(serializers.Serializer):
Expand Down Expand Up @@ -275,3 +277,24 @@ def drop_knowledge_index(knowledge_id=None, document_id=None):
sql = f'DROP INDEX "embedding_hnsw_idx_{k_id}"'
update_execute(sql, [])
maxkb_logger.info(f'Dropped index for knowledge ID: {k_id}')


def update_resource_mapping_by_knowledge(knowledge_id: str):
knowledge = QuerySet(Knowledge).filter(id=knowledge_id).first()
if knowledge.type == KnowledgeType.WORKFLOW:
knowledge_workflow_version = QuerySet(KnowledgeWorkflowVersion).filter(
knowledge_id=knowledge_id).order_by(
'-create_time')[0:1].first()
if knowledge_workflow_version:
other = ResourceMapping(source_type=ResourceType.KNOWLEDGE, target_type=ResourceType.MODEL,
source_id=knowledge.id,
target_id=knowledge.embedding_model_id)
save_workflow_mapping(knowledge_workflow_version.work_flow, ResourceType.KNOWLEDGE,
str(knowledge_id), [other])
return

QuerySet(ResourceMapping).filter(source_type=ResourceType.KNOWLEDGE,
source_id=knowledge.id).delete()
ResourceMapping(source_type=ResourceType.KNOWLEDGE, target_type=ResourceType.MODEL,
source_id=knowledge.id,
target_id=knowledge.embedding_model_id).save()
6 changes: 5 additions & 1 deletion apps/knowledge/serializers/knowledge.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
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
Expand All @@ -34,14 +35,16 @@
ProblemParagraphMapping, TaskType, State, SearchMode, KnowledgeFolder, File, Tag, KnowledgeWorkflow
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
GenerateRelatedSerializer, get_embedding_model_by_knowledge_id, list_paragraph, write_image, zip_dir, \
update_resource_mapping_by_knowledge
from knowledge.serializers.document import DocumentSerializers
from knowledge.task.embedding import embedding_by_knowledge, delete_embedding_by_knowledge
from knowledge.task.generate import generate_related_by_knowledge_id
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

Expand Down Expand Up @@ -373,6 +376,7 @@ def edit(self, instance: Dict, select_one=True):
KnowledgeEditRequest(data=instance).is_valid(knowledge=knowledge)
if 'embedding_model_id' in instance:
knowledge.embedding_model_id = instance.get('embedding_model_id')
update_resource_mapping_by_knowledge(self.data.get("knowledge_id"))
if "name" in instance:
knowledge.name = instance.get("name")
if 'desc' in instance:
Expand Down
8 changes: 6 additions & 2 deletions apps/knowledge/serializers/knowledge_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import Dict

import uuid_utils.compat as uuid
from django.core.cache import cache
from django.db import transaction
from django.db.models import QuerySet
from django.utils import timezone
Expand All @@ -14,6 +15,7 @@
from application.flow.i_step_node import KnowledgeWorkflowPostHandler
from application.flow.knowledge_workflow_manage import KnowledgeWorkflowManage
from application.flow.step_node import get_node
from application.flow.tools import save_workflow_mapping
from application.serializers.application import get_mcp_tools
from common.constants.cache_version import Cache_Version
from common.db.search import page_search
Expand All @@ -22,9 +24,10 @@
from common.utils.tool_code import ToolExecutor
from knowledge.models import KnowledgeScope, Knowledge, KnowledgeType, KnowledgeWorkflow, KnowledgeWorkflowVersion
from knowledge.models.knowledge_action import KnowledgeAction, State
from knowledge.serializers.common import update_resource_mapping_by_knowledge
from knowledge.serializers.knowledge import KnowledgeModelSerializer
from django.core.cache import cache
from system_manage.models import AuthTargetType
from system_manage.models.resource_mapping import ResourceType
from system_manage.serializers.user_resource_permission import UserResourcePermissionSerializer
from tools.models import Tool
from users.models import User
Expand Down Expand Up @@ -214,7 +217,7 @@ def save_workflow(self, instance: Dict):
)

knowledge_workflow.save()

save_workflow_mapping(instance.get('work_flow', {}), ResourceType.KNOWLEDGE, str(knowledge_id))
return {**KnowledgeModelSerializer(knowledge).data, 'document_list': []}

class Operate(serializers.Serializer):
Expand All @@ -241,6 +244,7 @@ def publish(self, with_valid=True):
QuerySet(KnowledgeWorkflow).filter(
knowledge_id=self.data.get("knowledge_id")
).update(is_publish=True, publish_time=timezone.now())
update_resource_mapping_by_knowledge(self.data.get("knowledge_id"))
return True

def edit(self, instance: Dict):
Expand Down
74 changes: 74 additions & 0 deletions apps/system_manage/migrations/0005_resourcemapping.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Generated by Django 5.2.8 on 2025-12-19 09:37

import uuid_utils.compat
from django.db import migrations, models

from knowledge.models import Knowledge


def resource_mapping(apps, schema_editor):
from system_manage.models.resource_mapping import ResourceMapping
from django.db.models import QuerySet
from application.flow.tools import get_workflow_resource, get_node_handle_callback, \
get_instance_resource
from system_manage.models.resource_mapping import ResourceType
from application.models import Application
from knowledge.models import KnowledgeWorkflow
resource_mapping_list = []
for application in QuerySet(Application):
workflow_mapping = get_workflow_resource(application.work_flow,
get_node_handle_callback(ResourceType.APPLICATION,
application.id))
instance_mapping = get_instance_resource(application, ResourceType.APPLICATION, str(application.id),
ResourceType.MODEL,
[lambda i: i.tts_model_id, lambda i: i.stt_model_id, ])
resource_mapping_list += workflow_mapping
resource_mapping_list += instance_mapping
knowledge_workflow_dict = {str(kw.knowledge_id): kw for kw in QuerySet(KnowledgeWorkflow)}
for knowledge in QuerySet(Knowledge):
knowledge_workflow = knowledge_workflow_dict.get(str(knowledge.id))
if knowledge_workflow:
workflow_mapping = get_workflow_resource(knowledge_workflow.work_flow,
get_node_handle_callback(ResourceType.KNOWLEDGE,
str(knowledge_workflow.knowledge_id)))
resource_mapping_list += workflow_mapping
instance_mapping = get_instance_resource(knowledge, ResourceType.KNOWLEDGE, str(knowledge.id),
ResourceType.MODEL,
[lambda i: i.embedding_model_id])

resource_mapping_list += instance_mapping

QuerySet(ResourceMapping).bulk_create(resource_mapping_list)


class Migration(migrations.Migration):
dependencies = [
('system_manage', '0004_alter_systemsetting_type_and_more'),
('knowledge', '0007_remove_knowledgeworkflowversion_workflow_and_more'),
('application', '0003_application_stt_model_params_setting_and_more'),
]

operations = [
migrations.CreateModel(
name='ResourceMapping',
fields=[
('create_time', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, db_index=True, verbose_name='修改时间')),
('id',
models.UUIDField(default=uuid_utils.compat.uuid7, editable=False, primary_key=True, serialize=False,
verbose_name='主键id')),
('source_type', models.CharField(
choices=[('KNOWLEDGE', '知识库'), ('APPLICATION', '应用'), ('TOOL', '工具'), ('MODEL', '模型')],
db_index=True, verbose_name='关联资源类型')),
('target_type', models.CharField(
choices=[('KNOWLEDGE', '知识库'), ('APPLICATION', '应用'), ('TOOL', '工具'), ('MODEL', '模型')],
db_index=True, verbose_name='被关联资源类型')),
('source_id', models.CharField(db_index=True, max_length=128, verbose_name='关联资源id')),
('target_id', models.CharField(db_index=True, max_length=128, verbose_name='被关联资源id')),
],
options={
'db_table': 'resource_mapping',
},
),
migrations.RunPython(resource_mapping)
]
Loading
Loading