mirror of
https://github.com/langgenius/dify.git
synced 2026-11-11 13:51:25 +08:00
184 lines
7.1 KiB
Python
184 lines
7.1 KiB
Python
"""Regression coverage for persisted historical Classic Agent tool logs."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from datetime import datetime
|
|
|
|
import pytest
|
|
from sqlalchemy.orm import Session
|
|
|
|
from models import App
|
|
from models.enums import CreatorUserRole
|
|
from models.model import AppMode, AppModelConfig, Conversation, ConversationFromSource, Message, MessageAgentThought
|
|
from services import agent_service
|
|
from tests.helpers.unit.model_factories import make_account, make_app, make_conversation, make_end_user, make_message
|
|
|
|
_NOW = datetime(2026, 1, 1)
|
|
|
|
|
|
def _persist_log(
|
|
session: Session, monkeypatch: pytest.MonkeyPatch, *, tool_still_configured: bool = False, end_user: bool = False
|
|
) -> App:
|
|
account = make_account(name="Executor", timezone="UTC")
|
|
config = AppModelConfig(
|
|
app_id="app-1",
|
|
model=json.dumps({"provider": "openai", "name": "gpt-4", "mode": "chat"}),
|
|
agent_mode=json.dumps(
|
|
{
|
|
"enabled": True,
|
|
"strategy": "react",
|
|
"tools": [
|
|
{"enabled": True, "tool_name": "old_tool", "provider_type": "api", "provider_id": "provider-id"}
|
|
]
|
|
if tool_still_configured
|
|
else [],
|
|
}
|
|
),
|
|
)
|
|
app = make_app(app_model_config_id=config.id, mode=AppMode.AGENT_CHAT)
|
|
source = ConversationFromSource.API if end_user else ConversationFromSource.CONSOLE
|
|
conversation = make_conversation(
|
|
mode=AppMode.AGENT_CHAT,
|
|
from_source=source,
|
|
from_account_id=None if end_user else account.id,
|
|
from_end_user_id="end-user-1" if end_user else None,
|
|
)
|
|
message = make_message(
|
|
from_source=source,
|
|
from_account_id=None if end_user else account.id,
|
|
from_end_user_id="end-user-1" if end_user else None,
|
|
inputs={},
|
|
query="Question",
|
|
message=[],
|
|
answer="Answer",
|
|
provider_response_latency=0.1,
|
|
answer_tokens=1,
|
|
message_tokens=1,
|
|
created_at=_NOW,
|
|
)
|
|
thought = MessageAgentThought(
|
|
message_id=message.id,
|
|
position=1,
|
|
created_by_role=CreatorUserRole.END_USER if end_user else CreatorUserRole.ACCOUNT,
|
|
created_by="end-user-1" if end_user else account.id,
|
|
tool="old_tool",
|
|
tool_meta_str=json.dumps({"old_tool": {"tool_config": {}}}),
|
|
tool_input="{}",
|
|
observation="{}",
|
|
thought="historical thought",
|
|
tokens=1,
|
|
)
|
|
session.add_all([account, config, app, conversation, message, thought])
|
|
if end_user:
|
|
session.add(make_end_user(app_id=app.id, name="Visitor"))
|
|
session.commit()
|
|
session.expire_all()
|
|
monkeypatch.setattr(agent_service, "current_user", account)
|
|
return app
|
|
|
|
|
|
@pytest.mark.parametrize("tool_still_configured", [False, True])
|
|
@pytest.mark.parametrize("end_user", [False, True])
|
|
def test_legacy_tool_log_without_provider_metadata_uses_configured_tool_or_blank_icon(
|
|
monkeypatch: pytest.MonkeyPatch, sqlite_session: Session, tool_still_configured: bool, end_user: bool
|
|
) -> None:
|
|
app = _persist_log(sqlite_session, monkeypatch, tool_still_configured=tool_still_configured, end_user=end_user)
|
|
calls: list[dict[str, str]] = []
|
|
|
|
def get_tool_icon(*, tenant_id: str, provider_type: str, provider_id: str) -> str:
|
|
calls.append({"tenant_id": tenant_id, "provider_type": provider_type, "provider_id": provider_id})
|
|
return "icon-url"
|
|
|
|
monkeypatch.setattr(agent_service.ToolManager, "get_tool_icon", get_tool_icon)
|
|
|
|
result = agent_service.AgentService.get_agent_logs(app, "conversation-1", "message-1", sqlite_session)
|
|
|
|
assert result["iterations"][0]["tool_calls"][0]["tool_icon"] == ("icon-url" if tool_still_configured else "")
|
|
assert result["meta"]["executor"] == ("Visitor" if end_user else "Executor")
|
|
assert result["meta"]["total_tokens"] == 2
|
|
assert result["meta"]["iterations"] == 1
|
|
expected_calls: list[dict[str, str]] = (
|
|
[{"tenant_id": "tenant-1", "provider_type": "api", "provider_id": "provider-id"}]
|
|
if tool_still_configured
|
|
else []
|
|
)
|
|
assert calls == expected_calls
|
|
|
|
|
|
@pytest.mark.parametrize("missing", ["conversation", "message"])
|
|
def test_agent_logs_reject_missing_records(
|
|
monkeypatch: pytest.MonkeyPatch, sqlite_session: Session, missing: str
|
|
) -> None:
|
|
app = _persist_log(sqlite_session, monkeypatch)
|
|
conversation_id = "missing" if missing == "conversation" else "conversation-1"
|
|
message_id = "missing" if missing == "message" else "message-1"
|
|
|
|
with pytest.raises(
|
|
ValueError, match="Conversation not found" if missing == "conversation" else "Message not found"
|
|
):
|
|
agent_service.AgentService.get_agent_logs(app, conversation_id, message_id, sqlite_session)
|
|
|
|
|
|
@pytest.mark.parametrize("foreign", ["conversation", "message"])
|
|
def test_agent_logs_reject_records_outside_the_requested_owner_chain(
|
|
monkeypatch: pytest.MonkeyPatch, sqlite_session: Session, foreign: str
|
|
) -> None:
|
|
app = _persist_log(sqlite_session, monkeypatch)
|
|
other_app = make_app(app_id="other-app")
|
|
other_conversation = make_conversation(
|
|
conversation_id="other-conversation", app_id=other_app.id, from_source=ConversationFromSource.CONSOLE
|
|
)
|
|
sqlite_session.add_all([other_app, other_conversation])
|
|
message = sqlite_session.get(Message, "message-1")
|
|
assert message is not None
|
|
message.app_id = other_app.id
|
|
if foreign == "conversation":
|
|
conversation = sqlite_session.get(Conversation, "conversation-1")
|
|
assert conversation is not None
|
|
conversation.app_id = other_app.id
|
|
else:
|
|
message.conversation_id = other_conversation.id
|
|
sqlite_session.commit()
|
|
sqlite_session.expire_all()
|
|
|
|
with pytest.raises(
|
|
ValueError, match="Conversation not found" if foreign == "conversation" else "Message not found"
|
|
):
|
|
agent_service.AgentService.get_agent_logs(app, "conversation-1", "message-1", sqlite_session)
|
|
|
|
|
|
def test_agent_logs_read_only_the_messages_thoughts_in_position_order(
|
|
monkeypatch: pytest.MonkeyPatch, sqlite_session: Session
|
|
) -> None:
|
|
app = _persist_log(sqlite_session, monkeypatch)
|
|
other_message = make_message(
|
|
message_id="other-message",
|
|
query="Other",
|
|
message=[],
|
|
answer="Other",
|
|
from_source=ConversationFromSource.CONSOLE,
|
|
)
|
|
sqlite_session.add(other_message)
|
|
for message_id, position, text in [
|
|
("message-1", 3, "last"),
|
|
(other_message.id, 0, "foreign"),
|
|
("message-1", 2, "middle"),
|
|
]:
|
|
sqlite_session.add(
|
|
MessageAgentThought(
|
|
message_id=message_id,
|
|
position=position,
|
|
created_by_role=CreatorUserRole.ACCOUNT,
|
|
created_by="account-1",
|
|
thought=text,
|
|
)
|
|
)
|
|
sqlite_session.commit()
|
|
sqlite_session.expire_all()
|
|
|
|
result = agent_service.AgentService.get_agent_logs(app, "conversation-1", "message-1", sqlite_session)
|
|
|
|
assert [iteration["thought"] for iteration in result["iterations"]] == ["historical thought", "middle", "last"]
|
|
assert result["meta"]["iterations"] == 3
|