Skip to content

Commit 78be028

Browse files
authored
feat: Add resource mapping table (#4546)
1 parent a0d48da commit 78be028

File tree

8 files changed

+241
-11
lines changed

8 files changed

+241
-11
lines changed

apps/application/flow/tools.py

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,8 @@ async def _yield_mcp_response(chat_model, message_list, mcp_servers, mcp_output_
315315
tools = await client.get_tools()
316316
agent = create_react_agent(chat_model, tools)
317317
recursion_limit = int(CONFIG.get("LANGCHAIN_GRAPH_RECURSION_LIMIT", '25'))
318-
response = agent.astream({"messages": message_list}, config={"recursion_limit": recursion_limit}, stream_mode='messages')
318+
response = agent.astream({"messages": message_list}, config={"recursion_limit": recursion_limit},
319+
stream_mode='messages')
319320

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

398399

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

428428
async def anext_async(agen):
429429
return await agen.__anext__()
430+
431+
432+
target_source_node_mapping = {
433+
'TOOL': {'tool-lib-node': lambda n: [n.get('properties').get('node_data').get('tool_lib_id')]},
434+
'MODEL': {'ai-chat-node': lambda n: [n.get('properties').get('node_data').get('model_id')],
435+
'question-node': lambda n: [n.get('properties').get('node_data').get('model_id')],
436+
'speech-to-text-node': lambda n: [n.get('properties').get('node_data').get('stt_model_id')],
437+
'text-to-speech-node': lambda n: [n.get('properties').get('node_data').get('tts_model_id')],
438+
'image-to-video-node': lambda n: [n.get('properties').get('node_data').get('model_id')],
439+
'image-generate-node': lambda n: [n.get('properties').get('node_data').get('model_id')],
440+
'intent-node': lambda n: [n.get('properties').get('node_data').get('model_id')],
441+
'image-understand-node': lambda n: [n.get('properties').get('node_data').get('model_id')],
442+
'parameter-extraction-node': lambda n: [n.get('properties').get('node_data').get('model_id')],
443+
'video-understand-node': lambda n: [n.get('properties').get('node_data').get('model_id')]
444+
},
445+
'KNOWLEDGE': {'search-knowledge-node': lambda n: n.get('properties').get('node_data').get('knowledge_id_list')},
446+
'APPLICATION': {
447+
'application-node': lambda n: [n.get('properties').get('node_data').get('application_id')]
448+
}
449+
}
450+
451+
452+
def get_node_handle_callback(source_type, source_id):
453+
def node_handle_callback(node):
454+
from system_manage.models.resource_mapping import ResourceMapping
455+
response = []
456+
for key, value in target_source_node_mapping.items():
457+
if node.get('type') in value:
458+
call = value.get(node.get('type'))
459+
target_source_id_list = call(node)
460+
for target_source_id in target_source_id_list:
461+
if target_source_id:
462+
response.append(ResourceMapping(source_type=source_type, target_type=key, source_id=source_id,
463+
target_id=target_source_id))
464+
return response
465+
466+
return node_handle_callback
467+
468+
469+
def get_workflow_resource(workflow, node_handle):
470+
response = []
471+
if 'nodes' in workflow:
472+
for node in workflow.get('nodes'):
473+
rs = node_handle(node)
474+
if rs:
475+
for r in rs:
476+
response.append(r)
477+
if node.get('type') == 'loop-node':
478+
r = get_workflow_resource(node.get('properties', {}).get('node_data', {}).get('loop_body'), node_handle)
479+
for rn in r:
480+
response.append(rn)
481+
return list({(str(item.target_type) + str(item.target_id)): item for item in response}.values())
482+
return []
483+
484+
485+
def get_instance_resource(instance, source_type, source_id, target_type, field_call_list):
486+
response = []
487+
from system_manage.models.resource_mapping import ResourceMapping
488+
for field_call in field_call_list:
489+
target_id = field_call(instance)
490+
if target_id:
491+
response.append(ResourceMapping(source_type=source_type, target_type=target_type, source_id=source_id,
492+
target_id=target_id))
493+
return response
494+
495+
496+
def save_workflow_mapping(workflow, source_type, source_id, other_resource_mapping=None):
497+
if not other_resource_mapping:
498+
other_resource_mapping = []
499+
from system_manage.models.resource_mapping import ResourceMapping
500+
from django.db.models import QuerySet
501+
QuerySet(ResourceMapping).filter(source_type=source_type, source_id=source_id).delete()
502+
resource_mapping_list = get_workflow_resource(workflow,
503+
get_node_handle_callback(source_type,
504+
source_id))
505+
if resource_mapping_list:
506+
resource_mapping_list += other_resource_mapping
507+
QuerySet(ResourceMapping).bulk_create(
508+
{(str(item.target_type) + str(item.target_id)): item for item in resource_mapping_list}.values())

apps/application/serializers/application.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from application.models.application import Application, ApplicationTypeChoices, ApplicationKnowledgeMapping, \
3131
ApplicationFolder, ApplicationVersion
3232
from application.models.application_access_token import ApplicationAccessToken
33+
from application.serializers.common import update_resource_mapping_by_application
3334
from common import result
3435
from common.cache_data.application_access_token_cache import del_application_access_token
3536
from common.database_model_manage.database_model_manage import DatabaseModelManage
@@ -780,6 +781,7 @@ def publish(self, instance, with_valid=True):
780781
application_access_token.save()
781782
else:
782783
access_token = application_access_token.access_token
784+
update_resource_mapping_by_application(self.data.get("application_id"))
783785
del_application_access_token(access_token)
784786
return self.one(with_valid=False)
785787

apps/application/serializers/common.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,18 @@
66
@date:2025/6/9 13:42
77
@desc:
88
"""
9-
import json
109
from typing import List
1110

1211
from django.core.cache import cache
1312
from django.db.models import QuerySet
14-
from django.utils.translation import gettext_lazy as _
1513
from django.utils import timezone
14+
from django.utils.translation import gettext_lazy as _
1615

17-
from application.chat_pipeline.step.chat_step.i_chat_step import PostResponseHandler
1816
from application.models import Application, ChatRecord, Chat, ApplicationVersion, ChatUserType, ApplicationTypeChoices, \
1917
ApplicationKnowledgeMapping
2018
from application.serializers.application_chat import ChatCountSerializer
2119
from common.constants.cache_version import Cache_Version
2220
from common.database_model_manage.database_model_manage import DatabaseModelManage
23-
from common.encoder.encoder import SystemEncoder
2421
from common.exception.app_exception import ChatException
2522
from knowledge.models import Document
2623
from models_provider.models import Model
@@ -167,7 +164,7 @@ def to_base_pipeline_manage_params(self):
167164
'mcp_output_enable': self.application.mcp_output_enable,
168165
}
169166

170-
def to_pipeline_manage_params(self, problem_text: str, post_response_handler: PostResponseHandler,
167+
def to_pipeline_manage_params(self, problem_text: str, post_response_handler,
171168
exclude_paragraph_id_list, chat_user_id: str, chat_user_type, stream=True,
172169
form_data=None):
173170
if form_data is None:
@@ -321,3 +318,19 @@ def get_cache(chat_id):
321318
if chat_info_dict:
322319
return ChatInfo.map_to_chat_info(chat_info_dict)
323320
return None
321+
322+
323+
def update_resource_mapping_by_application(application_id: str):
324+
from application.flow.tools import get_instance_resource, save_workflow_mapping
325+
from system_manage.models.resource_mapping import ResourceType
326+
application = QuerySet(Application).filter(id=application_id).first()
327+
instance_mapping = get_instance_resource(application, ResourceType.APPLICATION, str(application.id),
328+
ResourceType.MODEL,
329+
[lambda i: i.tts_model_id, lambda i: i.stt_model_id, ])
330+
if application.type == 'WORK_FLOW':
331+
save_workflow_mapping(application.work_flow, ResourceType.APPLICATION, str(application_id),
332+
instance_mapping)
333+
return
334+
else:
335+
save_workflow_mapping({}, ResourceType.APPLICATION, str(application_id),
336+
instance_mapping)

apps/knowledge/serializers/common.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,19 @@
1616
from django.utils.translation import gettext_lazy as _
1717
from rest_framework import serializers
1818

19+
from application.flow.tools import save_workflow_mapping
1920
from common.config.embedding_config import ModelManage
2021
from common.db.search import native_search
2122
from common.db.sql_execute import sql_execute, update_execute
2223
from common.exception.app_exception import AppApiException
2324
from common.utils.common import get_file_content
2425
from common.utils.fork import Fork
2526
from common.utils.logger import maxkb_logger
26-
from knowledge.models import Document
27+
from knowledge.models import Document, KnowledgeWorkflow, KnowledgeWorkflowVersion, KnowledgeType
2728
from knowledge.models import Paragraph, Problem, ProblemParagraphMapping, Knowledge, File
2829
from maxkb.conf import PROJECT_DIR
2930
from models_provider.tools import get_model, get_model_default_params
31+
from system_manage.models.resource_mapping import ResourceMapping, ResourceType
3032

3133

3234
class MetaSerializer(serializers.Serializer):
@@ -275,3 +277,24 @@ def drop_knowledge_index(knowledge_id=None, document_id=None):
275277
sql = f'DROP INDEX "embedding_hnsw_idx_{k_id}"'
276278
update_execute(sql, [])
277279
maxkb_logger.info(f'Dropped index for knowledge ID: {k_id}')
280+
281+
282+
def update_resource_mapping_by_knowledge(knowledge_id: str):
283+
knowledge = QuerySet(Knowledge).filter(id=knowledge_id).first()
284+
if knowledge.type == KnowledgeType.WORKFLOW:
285+
knowledge_workflow_version = QuerySet(KnowledgeWorkflowVersion).filter(
286+
knowledge_id=knowledge_id).order_by(
287+
'-create_time')[0:1].first()
288+
if knowledge_workflow_version:
289+
other = ResourceMapping(source_type=ResourceType.KNOWLEDGE, target_type=ResourceType.MODEL,
290+
source_id=knowledge.id,
291+
target_id=knowledge.embedding_model_id)
292+
save_workflow_mapping(knowledge_workflow_version.work_flow, ResourceType.KNOWLEDGE,
293+
str(knowledge_id), [other])
294+
return
295+
296+
QuerySet(ResourceMapping).filter(source_type=ResourceType.KNOWLEDGE,
297+
source_id=knowledge.id).delete()
298+
ResourceMapping(source_type=ResourceType.KNOWLEDGE, target_type=ResourceType.MODEL,
299+
source_id=knowledge.id,
300+
target_id=knowledge.embedding_model_id).save()

apps/knowledge/serializers/knowledge.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from django.utils.translation import gettext_lazy as _
2020
from rest_framework import serializers
2121

22+
from application.flow.tools import get_workflow_resource, get_node_handle_callback, save_workflow_mapping
2223
from application.models import ApplicationKnowledgeMapping
2324
from common.config.embedding_config import VectorStore
2425
from common.database_model_manage.database_model_manage import DatabaseModelManage
@@ -34,14 +35,16 @@
3435
ProblemParagraphMapping, TaskType, State, SearchMode, KnowledgeFolder, File, Tag, KnowledgeWorkflow
3536
from knowledge.serializers.common import ProblemParagraphManage, drop_knowledge_index, \
3637
get_embedding_model_id_by_knowledge_id, MetaSerializer, \
37-
GenerateRelatedSerializer, get_embedding_model_by_knowledge_id, list_paragraph, write_image, zip_dir
38+
GenerateRelatedSerializer, get_embedding_model_by_knowledge_id, list_paragraph, write_image, zip_dir, \
39+
update_resource_mapping_by_knowledge
3840
from knowledge.serializers.document import DocumentSerializers
3941
from knowledge.task.embedding import embedding_by_knowledge, delete_embedding_by_knowledge
4042
from knowledge.task.generate import generate_related_by_knowledge_id
4143
from knowledge.task.sync import sync_web_knowledge, sync_replace_web_knowledge
4244
from maxkb.conf import PROJECT_DIR
4345
from models_provider.models import Model
4446
from system_manage.models import WorkspaceUserResourcePermission, AuthTargetType
47+
from system_manage.models.resource_mapping import ResourceType, ResourceMapping
4548
from system_manage.serializers.user_resource_permission import UserResourcePermissionSerializer
4649
from users.serializers.user import is_workspace_manage
4750

@@ -373,6 +376,7 @@ def edit(self, instance: Dict, select_one=True):
373376
KnowledgeEditRequest(data=instance).is_valid(knowledge=knowledge)
374377
if 'embedding_model_id' in instance:
375378
knowledge.embedding_model_id = instance.get('embedding_model_id')
379+
update_resource_mapping_by_knowledge(self.data.get("knowledge_id"))
376380
if "name" in instance:
377381
knowledge.name = instance.get("name")
378382
if 'desc' in instance:

apps/knowledge/serializers/knowledge_workflow.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from typing import Dict, List
77

88
import uuid_utils.compat as uuid
9+
from django.core.cache import cache
910
from django.db import transaction
1011
from django.db.models import QuerySet
1112
from django.http import HttpResponse
@@ -17,6 +18,7 @@
1718
from application.flow.i_step_node import KnowledgeWorkflowPostHandler
1819
from application.flow.knowledge_workflow_manage import KnowledgeWorkflowManage
1920
from application.flow.step_node import get_node
21+
from application.flow.tools import save_workflow_mapping
2022
from application.serializers.application import get_mcp_tools
2123
from common.constants.cache_version import Cache_Version
2224
from common.db.search import page_search
@@ -28,9 +30,10 @@
2830
from common.utils.tool_code import ToolExecutor
2931
from knowledge.models import KnowledgeScope, Knowledge, KnowledgeType, KnowledgeWorkflow, KnowledgeWorkflowVersion
3032
from knowledge.models.knowledge_action import KnowledgeAction, State
33+
from knowledge.serializers.common import update_resource_mapping_by_knowledge
3134
from knowledge.serializers.knowledge import KnowledgeModelSerializer
32-
from django.core.cache import cache
3335
from system_manage.models import AuthTargetType
36+
from system_manage.models.resource_mapping import ResourceType
3437
from system_manage.serializers.user_resource_permission import UserResourcePermissionSerializer
3538
from tools.models import Tool, ToolScope
3639
from tools.serializers.tool import ToolExportModelSerializer
@@ -244,7 +247,7 @@ def save_workflow(self, instance: Dict):
244247
)
245248

246249
knowledge_workflow.save()
247-
250+
save_workflow_mapping(instance.get('work_flow', {}), ResourceType.KNOWLEDGE, str(knowledge_id))
248251
return {**KnowledgeModelSerializer(knowledge).data, 'document_list': []}
249252

250253
class Import(serializers.Serializer):
@@ -395,6 +398,7 @@ def publish(self, with_valid=True):
395398
QuerySet(KnowledgeWorkflow).filter(
396399
knowledge_id=self.data.get("knowledge_id")
397400
).update(is_publish=True, publish_time=timezone.now())
401+
update_resource_mapping_by_knowledge(self.data.get("knowledge_id"))
398402
return True
399403

400404
def edit(self, instance: Dict):
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Generated by Django 5.2.8 on 2025-12-19 09:37
2+
3+
import uuid_utils.compat
4+
from django.db import migrations, models
5+
6+
from knowledge.models import Knowledge
7+
8+
9+
def resource_mapping(apps, schema_editor):
10+
from system_manage.models.resource_mapping import ResourceMapping
11+
from django.db.models import QuerySet
12+
from application.flow.tools import get_workflow_resource, get_node_handle_callback, \
13+
get_instance_resource
14+
from system_manage.models.resource_mapping import ResourceType
15+
from application.models import Application
16+
from knowledge.models import KnowledgeWorkflow
17+
resource_mapping_list = []
18+
for application in QuerySet(Application):
19+
workflow_mapping = get_workflow_resource(application.work_flow,
20+
get_node_handle_callback(ResourceType.APPLICATION,
21+
application.id))
22+
instance_mapping = get_instance_resource(application, ResourceType.APPLICATION, str(application.id),
23+
ResourceType.MODEL,
24+
[lambda i: i.tts_model_id, lambda i: i.stt_model_id, ])
25+
resource_mapping_list += workflow_mapping
26+
resource_mapping_list += instance_mapping
27+
knowledge_workflow_dict = {str(kw.knowledge_id): kw for kw in QuerySet(KnowledgeWorkflow)}
28+
for knowledge in QuerySet(Knowledge):
29+
knowledge_workflow = knowledge_workflow_dict.get(str(knowledge.id))
30+
if knowledge_workflow:
31+
workflow_mapping = get_workflow_resource(knowledge_workflow.work_flow,
32+
get_node_handle_callback(ResourceType.KNOWLEDGE,
33+
str(knowledge_workflow.knowledge_id)))
34+
resource_mapping_list += workflow_mapping
35+
instance_mapping = get_instance_resource(knowledge, ResourceType.KNOWLEDGE, str(knowledge.id),
36+
ResourceType.MODEL,
37+
[lambda i: i.embedding_model_id])
38+
39+
resource_mapping_list += instance_mapping
40+
41+
QuerySet(ResourceMapping).bulk_create(resource_mapping_list)
42+
43+
44+
class Migration(migrations.Migration):
45+
dependencies = [
46+
('system_manage', '0004_alter_systemsetting_type_and_more'),
47+
('knowledge', '0007_remove_knowledgeworkflowversion_workflow_and_more'),
48+
('application', '0003_application_stt_model_params_setting_and_more'),
49+
]
50+
51+
operations = [
52+
migrations.CreateModel(
53+
name='ResourceMapping',
54+
fields=[
55+
('create_time', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='创建时间')),
56+
('update_time', models.DateTimeField(auto_now=True, db_index=True, verbose_name='修改时间')),
57+
('id',
58+
models.UUIDField(default=uuid_utils.compat.uuid7, editable=False, primary_key=True, serialize=False,
59+
verbose_name='主键id')),
60+
('source_type', models.CharField(
61+
choices=[('KNOWLEDGE', '知识库'), ('APPLICATION', '应用'), ('TOOL', '工具'), ('MODEL', '模型')],
62+
db_index=True, verbose_name='关联资源类型')),
63+
('target_type', models.CharField(
64+
choices=[('KNOWLEDGE', '知识库'), ('APPLICATION', '应用'), ('TOOL', '工具'), ('MODEL', '模型')],
65+
db_index=True, verbose_name='被关联资源类型')),
66+
('source_id', models.CharField(db_index=True, max_length=128, verbose_name='关联资源id')),
67+
('target_id', models.CharField(db_index=True, max_length=128, verbose_name='被关联资源id')),
68+
],
69+
options={
70+
'db_table': 'resource_mapping',
71+
},
72+
),
73+
migrations.RunPython(resource_mapping)
74+
]

0 commit comments

Comments
 (0)