refactor: 重构 AgentScope 运行时模块并优化前端附件展示

This commit is contained in:
qzl
2026-03-13 15:42:01 +08:00
parent a10a2db27a
commit 4c10929498
28 changed files with 1494 additions and 2163 deletions
@@ -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,
)
+28 -40
View File
@@ -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(