refactor: 重构 AgentScope 运行时模块并优化前端附件展示
This commit is contained in:
@@ -4,7 +4,7 @@ from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
import v1.agent.attachment_storage as attachment_storage_module
|
||||
from services.base.supabase import SupabaseService
|
||||
|
||||
|
||||
class _FakeBucket:
|
||||
@@ -34,9 +34,11 @@ class _FakeStorage:
|
||||
async def test_attachment_storage_rejects_unexpected_bucket(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
storage = attachment_storage_module.AgentAttachmentStorage()
|
||||
from core.config.settings import config as app_config
|
||||
|
||||
storage = SupabaseService()
|
||||
monkeypatch.setattr(
|
||||
attachment_storage_module.config.storage,
|
||||
app_config.storage,
|
||||
"bucket",
|
||||
"allowed-bucket",
|
||||
)
|
||||
@@ -54,16 +56,18 @@ async def test_attachment_storage_rejects_unexpected_bucket(
|
||||
async def test_attachment_storage_accepts_configured_bucket(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
storage = attachment_storage_module.AgentAttachmentStorage()
|
||||
from core.config.settings import config as app_config
|
||||
|
||||
storage = SupabaseService()
|
||||
fake_bucket = _FakeBucket()
|
||||
fake_client = SimpleNamespace(storage=_FakeStorage(fake_bucket))
|
||||
monkeypatch.setattr(
|
||||
attachment_storage_module.config.storage,
|
||||
app_config.storage,
|
||||
"bucket",
|
||||
"allowed-bucket",
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
attachment_storage_module.supabase_service,
|
||||
storage,
|
||||
"get_admin_client",
|
||||
lambda: fake_client,
|
||||
)
|
||||
|
||||
@@ -172,6 +172,12 @@ class _FakeAttachmentStorage:
|
||||
)
|
||||
return f"https://signed.example/{path}?exp={expires_in_seconds}"
|
||||
|
||||
def parse_signed_url(self, url: str) -> tuple[str, str]:
|
||||
if url.startswith("https://signed.example/"):
|
||||
path = url.replace("https://signed.example/", "").split("?")[0]
|
||||
return "agent-test-bucket", path
|
||||
raise RuntimeError("Invalid signed URL")
|
||||
|
||||
|
||||
class _AlwaysFailAttachmentStorage:
|
||||
async def upload_bytes(
|
||||
@@ -199,6 +205,10 @@ class _AlwaysFailAttachmentStorage:
|
||||
del bucket, path, expires_in_seconds
|
||||
raise RuntimeError("sign failed")
|
||||
|
||||
def parse_signed_url(self, url: str) -> tuple[str, str]:
|
||||
del url
|
||||
raise RuntimeError("parse failed")
|
||||
|
||||
|
||||
def _user() -> CurrentUser:
|
||||
return CurrentUser(
|
||||
@@ -358,7 +368,7 @@ async def test_enqueue_run_handles_session_create_race() -> None:
|
||||
assert repository.rolled_back is True
|
||||
|
||||
|
||||
async def test_enqueue_run_uses_forwarded_attachments_and_injects_metadata(
|
||||
async def test_enqueue_run_parses_signed_url_and_injects_metadata(
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
monkeypatch.setattr(
|
||||
@@ -386,62 +396,50 @@ async def test_enqueue_run_uses_forwarded_attachments_and_injects_metadata(
|
||||
{
|
||||
"type": "binary",
|
||||
"mimeType": "image/png",
|
||||
"url": "https://signed.example/upload.png",
|
||||
"url": "https://signed.example/agent-inputs/u/t/r/file.png",
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
"tools": [],
|
||||
"context": [],
|
||||
"forwardedProps": {
|
||||
"attachments": [
|
||||
{
|
||||
"bucket": "agent-test-bucket",
|
||||
"path": "agent-inputs/00000000-0000-0000-0000-000000000001/00000000-0000-0000-0000-000000000001/upload.png",
|
||||
"mimeType": "image/png",
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
accepted = await service.enqueue_run(run_input=run_input, current_user=_user())
|
||||
|
||||
assert accepted.task_id == "task-1"
|
||||
assert len(attachment_storage.calls) == 1
|
||||
download = attachment_storage.calls[0]
|
||||
assert download["bucket"] == "agent-test-bucket"
|
||||
assert download["download"] is True
|
||||
assert repository.persisted_user_messages
|
||||
persisted = repository.persisted_user_messages[0]
|
||||
assert persisted["session_id"] == "00000000-0000-0000-0000-000000000001"
|
||||
assert persisted["run_id"] == "run-with-image"
|
||||
metadata = persisted["metadata"]
|
||||
assert isinstance(metadata, dict)
|
||||
attachments = metadata.get("attachments")
|
||||
assert isinstance(attachments, list)
|
||||
assert attachments and isinstance(attachments[0], dict)
|
||||
assert attachments[0]["bucket"] == "agent-test-bucket"
|
||||
assert isinstance(attachments[0]["path"], str)
|
||||
attachments = metadata.get("user_message_attachments")
|
||||
assert isinstance(attachments, dict)
|
||||
assert attachments["bucket"] == "agent-test-bucket"
|
||||
assert attachments["path"] == "agent-inputs/u/t/r/file.png"
|
||||
assert attachments["mime_type"] == "image/png"
|
||||
|
||||
|
||||
async def test_enqueue_run_raises_when_attachment_download_fails_without_fallback(
|
||||
async def test_enqueue_run_with_invalid_signed_url_still_succeeds(
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
monkeypatch.setattr(
|
||||
agent_service_module.config.storage, "bucket", "agent-test-bucket"
|
||||
)
|
||||
repository = _FakeRepository()
|
||||
attachment_storage = _FakeAttachmentStorage()
|
||||
service = AgentService(
|
||||
repository=repository,
|
||||
queue=_FakeQueue(),
|
||||
stream=_FakeStream(),
|
||||
attachment_storage=_AlwaysFailAttachmentStorage(),
|
||||
attachment_storage=attachment_storage,
|
||||
)
|
||||
run_input = RunAgentInput.model_validate(
|
||||
{
|
||||
"threadId": "00000000-0000-0000-0000-000000000001",
|
||||
"runId": "run-with-image-fail",
|
||||
"runId": "run-with-invalid-url",
|
||||
"state": {},
|
||||
"messages": [
|
||||
{
|
||||
@@ -452,33 +450,23 @@ async def test_enqueue_run_raises_when_attachment_download_fails_without_fallbac
|
||||
{
|
||||
"type": "binary",
|
||||
"mimeType": "image/png",
|
||||
"url": "https://signed.example/upload.png",
|
||||
"url": "invalid-url-format",
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
"tools": [],
|
||||
"context": [],
|
||||
"forwardedProps": {
|
||||
"attachments": [
|
||||
{
|
||||
"bucket": "agent-test-bucket",
|
||||
"path": "agent-inputs/00000000-0000-0000-0000-000000000001/00000000-0000-0000-0000-000000000001/upload.png",
|
||||
"mimeType": "image/png",
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
try:
|
||||
await service.enqueue_run(run_input=run_input, current_user=_user())
|
||||
raise AssertionError("expected HTTPException")
|
||||
except HTTPException as exc:
|
||||
assert exc.status_code == 502
|
||||
assert exc.detail == "Failed to fetch attachment"
|
||||
accepted = await service.enqueue_run(run_input=run_input, current_user=_user())
|
||||
|
||||
assert repository.persisted_user_messages == []
|
||||
assert accepted.task_id == "task-1"
|
||||
assert repository.persisted_user_messages
|
||||
persisted = repository.persisted_user_messages[0]
|
||||
metadata = persisted["metadata"]
|
||||
assert metadata is None
|
||||
|
||||
|
||||
async def test_enqueue_run_rejects_unsupported_attachment_type(
|
||||
|
||||
Reference in New Issue
Block a user