deer-flow/backend/tests/test_managed_deepseek_live.py
YxinMiracle b8ab097ca2
fix(models): support official DeepSeek managed profiles (#5718)
Co-authored-by: YxinMiracle <“939157765@qq.com”>
2026-09-22 17:53:21 +08:00

76 lines
3.7 KiB
Python

"""Opt-in verification of the same managed DeepSeek path before/after a fix.
From backend/ (test credentials are read only from the process environment):
DEER_FLOW_RUN_LIVE_TESTS=1 uv run --no-sync pytest \
tests/test_managed_deepseek_live.py -q -s
Set DEEPSEEK_TEST_API_KEY separately; optional DEEPSEEK_TEST_MODEL defaults to
"deepseek-flash". This sends a few short requests to api.deepseek.com and may
incur charges. It never starts an agent or modifies the real managed catalog.
"""
import os
from types import SimpleNamespace
import pytest
from langchain_core.messages import HumanMessage, ToolMessage
from app.gateway.routers import managed_models as router
from deerflow.config.app_config import AppConfig
from deerflow.config.managed_models import ManagedModel
from deerflow.models.factory import create_chat_model
pytestmark = [
pytest.mark.live,
pytest.mark.skipif(
os.getenv("DEER_FLOW_RUN_LIVE_TESTS") != "1" or not os.getenv("DEEPSEEK_TEST_API_KEY") or bool(os.getenv("CI")),
reason="Requires explicit live opt-in and DEEPSEEK_TEST_API_KEY; never runs in CI",
),
]
@pytest.fixture
def profile(tmp_path, monkeypatch):
monkeypatch.setenv("DEER_FLOW_HOME", str(tmp_path))
monkeypatch.setenv("LANGSMITH_TRACING", "false")
monkeypatch.setenv("LANGCHAIN_TRACING_V2", "false")
return ManagedModel(name="deepseek-live", model=os.getenv("DEEPSEEK_TEST_MODEL", "deepseek-flash"), base_url="https://api.deepseek.com", api_key=os.environ["DEEPSEEK_TEST_API_KEY"], max_tokens=512)
@pytest.mark.asyncio
async def test_managed_deepseek_connection_probe_live(profile, tmp_path):
request = SimpleNamespace(state=SimpleNamespace(user=SimpleNamespace(system_role="admin")))
result = await router.test_model(request, router.SaveModelRequest(config=profile))
assert result == {"ok": True, "message": "success"}
assert not (tmp_path / "managed-models").exists()
@pytest.mark.asyncio
@pytest.mark.parametrize("thinking", [False, True])
async def test_managed_deepseek_tool_round_trip_live(profile, thinking, tmp_path):
config = AppConfig.model_validate({"sandbox": {"use": "test"}, "models": [profile.runtime_config().model_dump(exclude_none=True)]})
model = create_chat_model(profile.name, thinking_enabled=thinking, app_config=config, attach_tracing=False, timeout=30, max_retries=0)
tool = {"type": "function", "function": {"name": "connection_check", "description": "Check the connection", "parameters": {"type": "object", "properties": {}}}}
bound = model.bind_tools([tool])
history = [HumanMessage(content="Call connection_check exactly once. After its result, reply only OK.")]
first = None
async for chunk in bound.astream(history, config={"callbacks": []}):
first = chunk if first is None else first + chunk
assert first is not None and first.tool_calls
history.append(first)
history.extend(ToolMessage(content="Connection is healthy.", tool_call_id=call["id"]) for call in first.tool_calls)
payload = model._get_request_payload(history, **bound.kwargs)
assert payload["max_tokens"] == 512
assert "max_completion_tokens" not in payload
assert payload["extra_body"]["thinking"]["type"] == ("enabled" if thinking else "disabled")
assistant = payload["messages"][1]
assert assistant["content"] is not None
if thinking:
# Reasoning can be empty. Verify faithful replay, not model verbosity.
assert assistant["reasoning_content"] == first.additional_kwargs.get("reasoning_content", "")
final = None
async for chunk in bound.astream(history, config={"callbacks": []}):
final = chunk if final is None else final + chunk
assert final is not None and final.content and not final.tool_calls
assert not (tmp_path / "managed-models").exists()