deer-flow/backend/tests/test_openai_response_guard.py
Eilen Shin aaca3eeaeb
refactor(agent): ground runtime decisions in structured evidence (#7986)
* refactor(agent): ground runtime decisions in structured evidence

Use producer status and structured errors for tool recovery, preserve memory context without lexical confirmation, and remove predetermined receipt and skill conclusions.

Require explicit MCP discovery and authenticated schedule trial actions. Preserve authorization, ownership, isolation, and bounded execution; update documentation and regression coverage.

* test(sandbox): expect read_file initialization error callback

* fix(agent): address evidence review follow-ups

* fix(agent): preserve retry budgets for OpenAI transport errors

* fix(agent): expose tool execution status in model requests

Project validated execution metadata and exact tool text into request-only JSON so status survives provider serialization. Preserve stored results and receipt hashes, cover SDK and graph boundaries, and migrate replay input hashes without changing recorded responses.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-11-08 22:00:23 +08:00

42 lines
1.2 KiB
Python

from __future__ import annotations
import asyncio
import httpx
import pytest
from deerflow.models.openai_response import GuardedChatOpenAI
from deerflow.models.response_errors import EmptyModelResponseError
def test_http_200_with_empty_choices_raises_retryable_response_error():
transport = httpx.MockTransport(
lambda _request: httpx.Response(
200,
json={
"id": "chatcmpl-empty",
"object": "chat.completion",
"created": 1,
"model": "test-model",
"choices": [],
},
)
)
http_client = httpx.Client(transport=transport, trust_env=False)
http_async_client = httpx.AsyncClient(transport=transport, trust_env=False)
model = GuardedChatOpenAI(
model="test-model",
api_key="sk-test",
base_url="https://provider.invalid/v1",
max_retries=0,
http_client=http_client,
http_async_client=http_async_client,
)
try:
with pytest.raises(EmptyModelResponseError, match="empty choices"):
model.invoke("hello")
finally:
http_client.close()
asyncio.run(http_async_client.aclose())