icrim 93b0fd229c Release 0.2.0
Co-authored-by: xinchengxx <xinchengxx@deepseek.com>
Co-authored-by: zhu-he <hezhu@deepseek.com>
2026-10-20 00:21:22 +08:00

154 lines
5.3 KiB
Python

"""Smoke tests for the FastAPI example and its inference callback."""
import importlib.util
import json
from pathlib import Path
import pytest
from deepseek_recipe import (
ConversionOptions,
InferenceChunk,
ModelVersion,
SpecialToken,
WebSearchBehavior,
)
from fastapi.testclient import TestClient
SPEC = importlib.util.spec_from_file_location(
"recipe_server_example", Path(__file__).resolve().parents[1] / "main.py"
)
SERVER = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(SERVER)
PROTOCOL_REQUESTS = [
(
"/v1/chat/completions",
{"messages": [{"role": "user", "content": "hi"}]},
"data: [DONE]",
),
("/v1/responses", {"input": "hi"}, "event: response.completed"),
(
"/v1/messages",
{"messages": [{"role": "user", "content": "hi"}]},
"event: message_stop",
),
]
def response_text(path, body):
if path == "/v1/chat/completions":
return body["choices"][0]["message"]["content"]
if path == "/v1/responses":
return "".join(
content["text"]
for item in body["output"]
if item["type"] == "message"
for content in item["content"]
if content["type"] == "output_text"
)
return "".join(item["text"] for item in body["content"] if item["type"] == "text")
@pytest.mark.parametrize("path,payload,terminal", PROTOCOL_REQUESTS)
@pytest.mark.parametrize("stream", [False, True])
def test_example_server(path, payload, terminal, stream):
with TestClient(SERVER.app) as client:
response = client.post(
path, json={"model": "deepseek-flash", "stream": stream, **payload}
)
assert response.status_code == 200, response.text
assert "cache-control" not in response.headers
if stream:
assert response.headers["content-type"].startswith("text/event-stream")
assert "Hello " in response.text and "world!" in response.text
assert terminal in response.text
else:
assert response.headers["content-type"] == "application/json"
assert response_text(path, response.json()) == "Hello world!"
def test_custom_inference():
async def infer(request):
assert "hi" in request.prompt
assert request.inference_options.temperature == 0.25
assert request.images == []
assert request.image_token_adjustment == 0
yield InferenceChunk.text(SpecialToken.ThinkingEnd.default_str() + "Custom answer")
options = SERVER.conversion_options().with_model_alias(
"custom-model", ModelVersion.DeepseekV41Flash
)
with TestClient(SERVER.create_app(infer, options=options)) as client:
response = client.post(
"/v1/responses",
json={
"model": "custom-model",
"input": "hi",
"temperature": 0.25,
"reasoning": {"effort": "none"},
},
)
assert response.status_code == 200, response.text
assert response.json()["model"] == "custom-model"
assert response_text("/v1/responses", response.json()) == "Custom answer"
def test_invalid_request():
with TestClient(SERVER.app) as client:
response = client.post("/v1/chat/completions", json={})
assert response.status_code == 400
assert response.headers["content-type"] == "application/json"
assert "cache-control" not in response.headers
assert response.json()["error"]["type"] == "invalid_request_error"
def test_model_version_selects_the_prompt_encoding():
def body(model):
return json.dumps({
"model": model,
"messages": [
{"role": "system", "content": "Be brief."},
{"role": "user", "content": "hi"},
],
}).encode()
system_token = SpecialToken.System.default_str()
# V4.1 writes the system token; V4 writes no system token.
v41 = SERVER.prepare_request("chat_completions", body("deepseek-flash"))
v4 = SERVER.prepare_request("chat_completions", body("deepseek-v4-pro"))
assert system_token in v41.prompt
assert system_token not in v4.prompt
def test_web_search_conversion_options():
messages = {
"model": "deepseek-flash",
"stream": False,
"messages": [{"role": "user", "content": "hi"}],
"tools": [{"type": "web_search_20250305", "name": "web_search"}],
}
responses = {
"model": "deepseek-flash",
"stream": False,
"input": "hi",
"tools": [{"type": "web_search"}],
}
# The default options reject Messages web search and drop Responses web search.
with TestClient(SERVER.app) as client:
assert client.post("/v1/messages", json=messages).status_code == 400
assert client.post("/v1/responses", json=responses).status_code == 200
options = (
SERVER.conversion_options()
.with_responses_web_search(WebSearchBehavior.Reject)
.with_messages_web_search(WebSearchBehavior.Drop)
)
with TestClient(SERVER.create_app(options=options)) as client:
accepted = client.post("/v1/messages", json=messages)
rejected = client.post("/v1/responses", json=responses)
assert accepted.status_code == 200, accepted.text
assert response_text("/v1/messages", accepted.json()) == "Hello world!"
assert rejected.status_code == 400