Skip to content

Commit 5cbdc90

Browse files
committed
✨ Multi modal agent.
Pass the URL of the multimodal file as the query to the agent.
1 parent 3553949 commit 5cbdc90

File tree

8 files changed

+33
-35
lines changed

8 files changed

+33
-35
lines changed

backend/agents/create_agent_info.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from nexent.core.agents.agent_model import AgentRunInfo, ModelConfig, AgentConfig, ToolConfig
1010
from nexent.memory.memory_service import search_memory_in_levels
1111

12+
from services.file_management_service import get_llm_model
1213
from services.vectordatabase_service import (
1314
ElasticSearchService,
1415
get_vector_db_core,
@@ -25,7 +26,7 @@
2526
from utils.model_name_utils import add_repo_to_name
2627
from utils.prompt_template_utils import get_agent_prompt_template
2728
from utils.config_utils import tenant_config_manager, get_model_name_from_config
28-
from consts.const import LOCAL_MCP_SERVER, MODEL_CONFIG_MAPPING, LANGUAGE
29+
from consts.const import LOCAL_MCP_SERVER, MODEL_CONFIG_MAPPING, LANGUAGE, DATA_PROCESS_SERVICE
2930

3031
logger = logging.getLogger("create_agent_info")
3132
logger.setLevel(logging.DEBUG)
@@ -243,6 +244,12 @@ async def create_tool_config_list(agent_id, tenant_id, user_id):
243244
"vlm_model": get_vlm_model(tenant_id=tenant_id),
244245
"storage_client": minio_client,
245246
}
247+
elif tool_config.class_name == "AnalyzeTextFileTool":
248+
tool_config.metadata = {
249+
"llm_model": get_llm_model(tenant_id=tenant_id),
250+
"storage_client": minio_client,
251+
"data_process_service_url": DATA_PROCESS_SERVICE
252+
}
246253

247254
tool_config_list.append(tool_config)
248255

@@ -307,8 +314,8 @@ async def join_minio_file_description_to_query(minio_files, query):
307314
if minio_files and isinstance(minio_files, list):
308315
file_descriptions = []
309316
for file in minio_files:
310-
if isinstance(file, dict) and "description" in file and file["description"]:
311-
file_descriptions.append(file["description"])
317+
if isinstance(file, dict) and "url" in file and file["url"] and "name" in file and file["name"]:
318+
file_descriptions.append("File S3 URL: " + "s3:/" + file["url"] + ", file name:" + file["name"])
312319

313320
if file_descriptions:
314321
final_query = "User provided some reference files:\n"

backend/services/file_management_service.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -279,15 +279,8 @@ async def preprocess_files_generator(
279279
if "error" in file_data:
280280
raise Exception(file_data["error"])
281281

282-
if file_data["ext"] in ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']:
283-
# description = await process_image_file(query, file_data["filename"], file_data["content"], tenant_id, language)
284-
# truncation_percentage = None
285-
description = ""
286-
truncation_percentage = None
287-
else:
288-
# description, truncation_percentage = await process_text_file(query, file_data["filename"], file_data["content"], tenant_id, language)
289-
description = ""
290-
truncation_percentage = None
282+
description = ""
283+
truncation_percentage = None
291284
file_descriptions.append(description)
292285

293286
# Send processing result for each file

backend/services/tool_configuration_service.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import jsonref
1212
from mcpadapt.smolagents_adapter import _sanitize_function_name
1313

14-
from consts.const import DEFAULT_USER_ID, LOCAL_MCP_SERVER
14+
from consts.const import DEFAULT_USER_ID, LOCAL_MCP_SERVER, DATA_PROCESS_SERVICE
1515
from consts.exceptions import MCPConnectionError, ToolExecutionException, NotFoundException
1616
from consts.model import ToolInstanceInfoRequest, ToolInfo, ToolSourceEnum, ToolValidateRequest
1717
from database.remote_mcp_db import get_mcp_records_by_tenant, get_mcp_server_by_name_and_tenant
@@ -23,6 +23,7 @@
2323
search_last_tool_instance_by_tool_id,
2424
)
2525
from database.user_tenant_db import get_all_tenant_ids
26+
from services.file_management_service import get_llm_model
2627
from services.vectordatabase_service import get_embedding_model, get_vector_db_core
2728
from services.tenant_config_service import get_selected_knowledge_list
2829
from database.client import minio_client
@@ -615,6 +616,17 @@ def _validate_local_tool(
615616
'embedding_model': embedding_model,
616617
}
617618
tool_instance = tool_class(**params)
619+
elif tool_name == "analyze_text_file":
620+
if not tenant_id or not user_id:
621+
raise ToolExecutionException(f"Tenant ID and User ID are required for {tool_name} validation")
622+
long_text_to_text_model = get_llm_model(tenant_id=tenant_id)
623+
params = {
624+
**instantiation_params,
625+
'llm_model': long_text_to_text_model,
626+
'storage_client': minio_client,
627+
"data_process_service_url": DATA_PROCESS_SERVICE
628+
}
629+
tool_instance = tool_class(**params)
618630
elif tool_name == "analyze_image":
619631
if not tenant_id or not user_id:
620632
raise ToolExecutionException(f"Tenant ID and User ID are required for {tool_name} validation")

frontend/server.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const handle = app.getRequestHandler();
1313
const HTTP_BACKEND = process.env.HTTP_BACKEND || 'http://localhost:5010'; // config
1414
const WS_BACKEND = process.env.WS_BACKEND || 'ws://localhost:5014'; // runtime
1515
const RUNTIME_HTTP_BACKEND = process.env.RUNTIME_HTTP_BACKEND || 'http://localhost:5014'; // runtime
16-
const MINIO_BACKEND = process.env.MINIO_ENDPOINT || 'http://localhost:9000';
16+
const MINIO_BACKEND = process.env.MINIO_ENDPOINT || 'http://localhost:9010';
1717
const PORT = 3000;
1818

1919
const proxy = createProxyServer();

sdk/nexent/core/tools/analyze_image_tool.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,6 @@ def _forward_impl(self, image_urls_list: List[bytes], query: str) -> List[str]:
9999
if self.observer:
100100
running_prompt = self.running_prompt_zh if self.observer.lang == "zh" else self.running_prompt_en
101101
self.observer.add_message("", ProcessType.TOOL, running_prompt)
102-
card_content = [{"icon": "image", "text": f"Analyzing images..."}]
103-
self.observer.add_message("", ProcessType.CARD, json.dumps(card_content, ensure_ascii=False))
104102

105103
if image_urls_list is None:
106104
raise ValueError("image_urls cannot be None")

sdk/nexent/core/tools/analyze_text_file_tool.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,6 @@ def _forward_impl(
104104
if self.observer:
105105
running_prompt = self.running_prompt_zh if self.observer.lang == "zh" else self.running_prompt_en
106106
self.observer.add_message("", ProcessType.TOOL, running_prompt)
107-
card_content = [{"icon": "file", "text": f"Analyzing file..."}]
108-
self.observer.add_message("", ProcessType.CARD, json.dumps(card_content, ensure_ascii=False))
109107

110108
if file_url_list is None:
111109
raise ValueError("file_url_list cannot be None")

test/backend/agents/test_create_agent_info.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,15 +1276,18 @@ class TestJoinMinioFileDescriptionToQuery:
12761276
async def test_join_minio_file_description_to_query_with_files(self):
12771277
"""Test case with file descriptions"""
12781278
minio_files = [
1279-
{"description": "File 1 description"},
1280-
{"description": "File 2 description"},
1281-
{"no_description": "should be ignored"}
1279+
{"url": "/nexent/1.pdf", "name": "1.pdf"},
1280+
{"url": "/nexent/2.pdf", "name": "2.pdf"},
1281+
{"url": "/nexent/3.pdf", "name": "3.pdf"},
12821282
]
12831283
query = "test query"
12841284

12851285
result = await join_minio_file_description_to_query(minio_files, query)
12861286

1287-
expected = "User provided some reference files:\nFile 1 description\nFile 2 description\n\nUser wants to answer questions based on the above information: test query"
1287+
expected = ("User provided some reference files:\nFile S3 URL: s3://nexent/1.pdf, file name:1.pdf\n"
1288+
"File S3 URL: s3://nexent/2.pdf, file name:2.pdf\n"
1289+
"File S3 URL: s3://nexent/3.pdf, file name:3.pdf\n\n"
1290+
'User wants to answer questions based on the above information: test query')
12881291
assert result == expected
12891292

12901293
@pytest.mark.asyncio

test/sdk/core/tools/test_analyze_text_file_tool.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,6 @@ def tool(observer_zh, llm_model):
5656

5757

5858
class TestAnalyzeTextFileTool:
59-
def test_forward_impl_success_records_observer(self, tool, llm_model, observer_zh):
60-
tool.process_text_file = MagicMock(return_value="Extracted text")
61-
tool.analyze_file = MagicMock(return_value=("analysis", 0.0))
62-
63-
result = tool._forward_impl([b"file-bytes"], "Why?")
64-
65-
assert result == ["analysis"]
66-
tool.process_text_file.assert_called_once_with("file_1.txt", b"file-bytes")
67-
tool.analyze_file.assert_called_once_with("Why?", "Extracted text")
68-
observer_zh.add_message.assert_any_call("", ProcessType.TOOL, "正在分析文件...")
69-
observer_zh.add_message.assert_any_call("", ProcessType.CARD, json.dumps(
70-
[{"icon": "file", "text": "Analyzing file..."}], ensure_ascii=False))
71-
7259
def test_forward_impl_switches_language(self, observer_en, llm_model, monkeypatch):
7360
tool = AnalyzeTextFileTool(
7461
storage_client=MagicMock(),

0 commit comments

Comments
 (0)