Skip to content

✨ Feat: Add presigned URL support for external MCP tool file access and improve agent execution flow#2839

Merged
Dallas98 merged 13 commits intodevelopfrom
xyq/feat_file_url
Apr 23, 2026
Merged

✨ Feat: Add presigned URL support for external MCP tool file access and improve agent execution flow#2839
Dallas98 merged 13 commits intodevelopfrom
xyq/feat_file_url

Conversation

@xuyaqist
Copy link
Copy Markdown
Contributor

@xuyaqist xuyaqist commented Apr 22, 2026

Feat: Add presigned URL support for external MCP tool file access and improve agent execution flow

  • Add presigned URL generation during file uploads with 24-hour validity
    • Extended upload_file() and upload_fileobj() to generate presigned URLs
    • Changed default URL expiration from 1 hour to 24 hours
    • Added generate_presigned_url and presigned_url_expires parameters
image
  • Update agent prompts to prevent fabricated observation results(避免在思考阶段,模型没有执行代码,但模拟输出了结果造成模型幻觉的问题)

    • Removed manual "Observe Results:" output from execution flow
    • System now provides "Observation:" marker with real execution results
    • Added MCP tool markers ([MCP]) in tool listings
    • Added file URL usage guide for MCP vs internal tools
  • Include historical file attachments in agent context

    • Pass minio_files from conversation history to agent
    • Enables agent to reference files from previous conversation turns
image
  • Update frontend to handle presigned URLs
    • Store and transmit presigned_url for each attachment
    • Updated TypeScript types to include presigned_url field

Copilot AI review requested due to automatic review settings April 22, 2026 09:36
@xuyaqist xuyaqist changed the base branch from main to develop April 22, 2026 09:37
@xuyaqist xuyaqist changed the title Feat: Add presigned URL support for external MCP tool file access and improve agent execution flow ✨ Feat: Add presigned URL support for external MCP tool file access and improve agent execution flow Apr 22, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds presigned download URL support (default 24h) for externally hosted tools (e.g., MCP) to access uploaded files, updates agent prompts/execution guidance to avoid fabricated “observation” content, and extends agent context so historical attachments from conversation history are available during runs.

Changes:

  • Backend: generate/propagate presigned URLs on upload, and extend default URL expiry from 1h → 24h across storage URL helpers/endpoints.
  • Agent/runtime: include historical minio_files in the agent’s prompt context and update prompts to rely on system-provided Observation: outputs.
  • Frontend: persist/transmit presigned_url on attachments and pass historical attachment metadata in history.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
test/backend/database/test_attachment_db.py Updates upload tests to cover presigned URL behavior and new flags.
frontend/types/chat.ts Extends attachment types to include presigned_url.
frontend/lib/chatMessageExtractor.ts Preserves presigned_url when hydrating messages from API responses.
frontend/lib/chat/chatAttachmentUtils.ts Captures presigned URLs from upload results and attaches them to outgoing message metadata.
frontend/app/[locale]/chat/internal/chatInterface.tsx Sends presigned URLs + historical attachments in the agent run payload.
backend/prompts/utils/prompt_generate_zh.yaml Removes manual “Observe Results” pattern; relies on system Observation: marker.
backend/prompts/utils/prompt_generate_en.yaml Same as above for English.
backend/prompts/manager_system_prompt_template_zh.yaml Adds [MCP] markers and file URL usage guidance in manager prompt.
backend/prompts/manager_system_prompt_template_en.yaml Same as above for English.
backend/prompts/managed_system_prompt_template_zh.yaml Adds [MCP] markers and file URL usage guidance in managed prompt.
backend/prompts/managed_system_prompt_template_en.yaml Same as above for English.
backend/database/client.py Changes default presigned URL expiry to 24h.
backend/database/attachment_db.py Adds presigned URL generation to uploads; updates URL expiry defaults and list behavior.
backend/apps/file_management_app.py Updates API defaults/docs for URL expiry to 24h.
backend/agents/create_agent_info.py Appends both S3 and presigned URLs to query context and includes historical files.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 195 to +212
@patch('backend.database.attachment_db.os.path.exists')
@patch('backend.database.attachment_db.os.path.getsize')
@patch('backend.database.attachment_db.os.path.basename')
def test_upload_file_nonexistent_file(self, mock_basename, mock_getsize, mock_exists):
"""Test upload_file with nonexistent file"""
@patch('backend.database.attachment_db.get_file_url')
def test_upload_file_without_presigned_url(self, mock_get_file_url, mock_basename, mock_getsize, mock_exists):
"""Test upload_file when generate_presigned_url is False"""
mock_basename.return_value = 'test.txt'
mock_exists.return_value = False
mock_getsize.return_value = 0
mock_exists.return_value = True
mock_getsize.return_value = 1024
minio_client_mock.upload_file.return_value = (True, '/bucket/attachments/test.txt')

result = upload_file('/path/to/test.txt', 'attachments/test.txt', 'bucket', generate_presigned_url=False)

assert result['success'] is True
assert 'url' in result
assert 'presigned_url' not in result
mock_get_file_url.assert_not_called()

Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous test that covered the “nonexistent file” path for upload_file() appears to have been replaced. That branch (file_size falling back to 0 when os.path.exists is false) is still present in upload_file(), so it would be good to keep a dedicated test for the nonexistent-file scenario in addition to the new presigned-URL tests.

Copilot uses AI. Check for mistakes.
Comment on lines 273 to 287
@patch('backend.database.attachment_db.generate_object_name')
def test_upload_fileobj_preserves_file_position(self, mock_generate):
@patch('backend.database.attachment_db.get_file_url')
def test_upload_fileobj_preserves_file_position(self, mock_get_file_url, mock_generate):
"""Test upload_fileobj preserves original file position"""
mock_generate.return_value = 'attachments/test.txt'
minio_client_mock.upload_fileobj.return_value = (True, '/bucket/attachments/test.txt')
mock_get_file_url.return_value = {'success': True, 'url': 'http://minio:9000/presigned-url'}

file_obj = BytesIO(b'test data')
original_pos = 4
file_obj.seek(original_pos)

result = upload_fileobj(file_obj, 'test.txt', 'bucket')

# File position should be restored
assert file_obj.tell() == original_pos
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test_upload_fileobj_preserves_file_position verifies the stream position is restored, but it doesn’t assert that the upload reads from the beginning of the file (it uses a stubbed upload_fileobj that never reads). Since upload_fileobj() currently passes the stream through as-is, a non-zero starting position could lead to truncated uploads without this test catching it. Consider strengthening this test by using an upload_fileobj mock that reads file_obj and asserting the full content was uploaded while still restoring the original position.

Copilot uses AI. Check for mistakes.
Comment thread frontend/lib/chat/chatAttachmentUtils.ts
Comment thread backend/agents/create_agent_info.py
Comment on lines +441 to +448
.map((msg) => {
const historyItem: any = {
role: msg.role,
content:
msg.role === ROLE_ASSISTANT
? msg.finalAnswer?.trim() || msg.content || ""
: msg.content || "",
};
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new history mapping uses any for historyItem, which drops type safety for the request payload (and makes it easy to accidentally send malformed history). Consider introducing an explicit type for the history items (including optional minio_files) instead of any.

Copilot uses AI. Check for mistakes.
Comment on lines +451 to +460
if (msg.attachments && msg.attachments.length > 0) {
historyItem.minio_files = msg.attachments.map((attachment) => ({
object_name: attachment.object_name || "",
name: attachment.name,
type: attachment.type,
size: attachment.size,
url: attachment.url || "",
presigned_url: attachment.presigned_url || "",
description: attachment.description || "",
}));
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When constructing historyItem.minio_files, optional fields are forced to empty strings (""). This increases payload size and makes it harder for the backend to distinguish “missing” vs “present but empty”. Prefer leaving fields as undefined (so they’re omitted from JSON) or conditionally adding keys only when values exist.

Copilot uses AI. Check for mistakes.
Comment thread frontend/types/chat.ts Outdated
Comment thread frontend/types/chat.ts Outdated
xuyaqist and others added 5 commits April 23, 2026 10:27
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 23, 2026

Codecov Report

❌ Patch coverage is 90.00000% with 6 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
backend/agents/create_agent_info.py 89.74% 1 Missing and 3 partials ⚠️
backend/database/attachment_db.py 86.66% 0 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

@Dallas98 Dallas98 merged commit 98a9509 into develop Apr 23, 2026
15 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants