Co-authored-by: Clément Sirieix <clement.sirieix@mistral.ai>
Co-authored-by: Guillaume LE GOFF <guillaume.lgf@gmail.com>
Co-authored-by: Hdandria <henri.dandria@mistral.ai>
Co-authored-by: Ivana Dunisijevic <ivana.dunisijevic@mistral.ai>
Co-authored-by: Jean Burellier <sheplu@users.noreply.github.com>
Co-authored-by: Mathias Gesbert <mathias.gesbert@mistral.ai>
Co-authored-by: Mert Unsal <mert.unsal@mistral.ai>
Co-authored-by: Michel Thomazo <51709227+michelTho@users.noreply.github.com>
Co-authored-by: Paul VEZIA <166131032+le-codeur-rapide@users.noreply.github.com>
Co-authored-by: Pierre Rossinès <pierre.rossines@mistral.ai>
Co-authored-by: Val <102326092+vdeva@users.noreply.github.com>
Co-authored-by: Vincent G <10739306+VinceOPS@users.noreply.github.com>
Co-authored-by: renovate-mistral[bot] <253709520+renovate-mistral[bot]@users.noreply.github.com>
Co-authored-by: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
Clément Drouin 2026-06-19 11:01:24 +02:00 committed by GitHub
parent 564a14365e
commit 6bedf271ce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
223 changed files with 10533 additions and 6947 deletions

View file

@ -0,0 +1,132 @@
from __future__ import annotations
import json
from typing import Any
from tests.backend.data import Chunk, JsonResponse
def _sse_event(data: dict[str, Any]) -> Chunk:
return f"data: {json.dumps(data, separators=(',', ':'))}".encode()
def anthropic_request_content_blocks(payload: dict[str, Any]) -> list[dict[str, Any]]:
# Flatten every structured content block across a request's messages
# (text/thinking/tool_use/tool_result blocks).
return [
block
for message in payload["messages"]
if isinstance(message["content"], list)
for block in message["content"]
]
def anthropic_message(
text: str,
*,
input_tokens: int = 12,
output_tokens: int = 3,
stop_reason: str = "end_turn",
) -> JsonResponse:
return {
"content": [{"type": "text", "text": text}],
"usage": {"input_tokens": input_tokens, "output_tokens": output_tokens},
"stop_reason": stop_reason,
}
def anthropic_tool_use(
name: str,
tool_input: dict[str, Any],
*,
tool_id: str = "toolu_1",
input_tokens: int = 20,
output_tokens: int = 5,
) -> JsonResponse:
return {
"content": [
{"type": "tool_use", "id": tool_id, "name": name, "input": tool_input}
],
"usage": {"input_tokens": input_tokens, "output_tokens": output_tokens},
"stop_reason": "tool_use",
}
def anthropic_reasoning_tool_use_stream(
name: str,
arguments: str,
*,
reasoning: str = "thinking...",
signature: str = "sig",
tool_id: str = "toolu_1",
input_tokens: int = 20,
output_tokens: int = 5,
) -> list[Chunk]:
return [
_sse_event(e)
for e in (
{
"type": "message_start",
"message": {"usage": {"input_tokens": input_tokens}},
},
{
"type": "content_block_start",
"index": 0,
"content_block": {"type": "thinking", "thinking": reasoning},
},
{
"type": "content_block_delta",
"index": 0,
"delta": {"type": "signature_delta", "signature": signature},
},
{"type": "content_block_stop", "index": 0},
{
"type": "content_block_start",
"index": 1,
"content_block": {"type": "tool_use", "id": tool_id, "name": name},
},
{
"type": "content_block_delta",
"index": 1,
"delta": {"type": "input_json_delta", "partial_json": arguments},
},
{"type": "content_block_stop", "index": 1},
{
"type": "message_delta",
"delta": {"stop_reason": "tool_use"},
"usage": {"output_tokens": output_tokens},
},
{"type": "message_stop"},
)
]
def anthropic_text_stream(
text: str, *, input_tokens: int = 12, output_tokens: int = 3
) -> list[Chunk]:
return [
_sse_event(e)
for e in (
{
"type": "message_start",
"message": {"usage": {"input_tokens": input_tokens}},
},
{
"type": "content_block_start",
"index": 0,
"content_block": {"type": "text", "text": ""},
},
{
"type": "content_block_delta",
"index": 0,
"delta": {"type": "text_delta", "text": text},
},
{"type": "content_block_stop", "index": 0},
{
"type": "message_delta",
"delta": {"stop_reason": "end_turn"},
"usage": {"output_tokens": output_tokens},
},
{"type": "message_stop"},
)
]

View file

@ -1,7 +1,41 @@
from __future__ import annotations
from typing import Any
from tests.backend.data import Chunk, JsonResponse, ResultData, Url
def mistral_completion(
content: str,
*,
prompt_tokens: int = 100,
completion_tokens: int = 50,
tool_calls: list[dict[str, Any]] | None = None,
) -> JsonResponse:
return {
"id": "cmpl_test",
"created": 1234567890,
"model": "devstral-latest",
"object": "chat.completion",
"usage": {
"prompt_tokens": prompt_tokens,
"completion_tokens": completion_tokens,
"total_tokens": prompt_tokens + completion_tokens,
},
"choices": [
{
"index": 0,
"finish_reason": "tool_calls" if tool_calls else "stop",
"message": {
"role": "assistant",
"content": content,
"tool_calls": tool_calls,
},
}
],
}
SIMPLE_CONVERSATION_PARAMS: list[tuple[Url, JsonResponse, ResultData]] = [
(
"https://api.mistral.ai",

View file

@ -4,8 +4,7 @@ import json
from typing import Any
from tests.backend.data import Chunk, JsonResponse, ResultData, Url
OPENAI_RESPONSES_TEST_BASE_URL = "https://api.openai.com"
from tests.constants import OPENAI_BASE_URL
def _sse_event(data: dict[str, Any] | str) -> Chunk:
@ -90,9 +89,134 @@ def _function_call_item(
}
def openai_message_item(
text: str, *, phase: str | None = None, message_id: str = "msg_1"
) -> dict[str, Any]:
return _message_output_item(message_id, text, phase=phase)
def openai_function_call_item(
name: str, arguments: str, *, item_id: str = "fc_1", call_id: str = "call_1"
) -> dict[str, Any]:
return _function_call_item(item_id, call_id, name, arguments)
def openai_response(
output: list[dict[str, Any]],
*,
input_tokens: int = 10,
output_tokens: int = 2,
response_id: str = "resp_1",
model: str = "gpt-test",
) -> JsonResponse:
return {
"id": response_id,
"object": "response",
"created_at": 1234567890,
"model": model,
"output": output,
"usage": {
"input_tokens": input_tokens,
"output_tokens": output_tokens,
"total_tokens": input_tokens + output_tokens,
},
}
def openai_sse(*events: dict[str, Any] | str) -> list[Chunk]:
return [_sse_event(event) for event in events]
def openai_reasoning_tool_call_stream(
name: str,
arguments: str,
*,
reasoning: str = "thinking...",
call_id: str = "call_1",
item_id: str = "fc_1",
input_tokens: int = 20,
output_tokens: int = 5,
) -> list[Chunk]:
return openai_sse(
{"type": "response.created", "response": {"id": "resp_1", "output": []}},
{
"type": "response.output_item.added",
"output_index": 0,
"item": _stream_message_item("msg_r", phase="commentary"),
},
{
"type": "response.reasoning_summary_text.delta",
"output_index": 0,
"delta": reasoning,
},
{
"type": "response.output_item.added",
"output_index": 1,
"item": _function_call_item(
item_id, call_id, name, "", status="in_progress"
),
},
{
"type": "response.function_call_arguments.delta",
"output_index": 1,
"call_id": call_id,
"delta": arguments,
},
{
"type": "response.function_call_arguments.done",
"output_index": 1,
"call_id": call_id,
"name": name,
"arguments": arguments,
},
{
"type": "response.output_item.done",
"output_index": 1,
"item": _function_call_item(item_id, call_id, name, arguments),
},
{
"type": "response.completed",
"response": openai_response(
[_function_call_item(item_id, call_id, name, arguments)],
input_tokens=input_tokens,
output_tokens=output_tokens,
),
},
"[DONE]",
)
def openai_text_stream(
text: str, *, input_tokens: int = 10, output_tokens: int = 2
) -> list[Chunk]:
return openai_sse(
{"type": "response.created", "response": {"id": "resp_1", "output": []}},
{
"type": "response.output_item.added",
"output_index": 0,
"item": _stream_message_item("msg_1"),
},
{
"type": "response.output_text.delta",
"output_index": 0,
"content_index": 0,
"delta": text,
},
{
"type": "response.completed",
"response": openai_response(
[openai_message_item(text)],
input_tokens=input_tokens,
output_tokens=output_tokens,
),
},
"[DONE]",
)
SIMPLE_CONVERSATION_PARAMS: list[tuple[Url, JsonResponse, ResultData]] = [
(
OPENAI_RESPONSES_TEST_BASE_URL,
OPENAI_BASE_URL,
{
"id": "resp_fake_id_1234",
"object": "response",
@ -113,7 +237,7 @@ SIMPLE_CONVERSATION_PARAMS: list[tuple[Url, JsonResponse, ResultData]] = [
TOOL_CONVERSATION_PARAMS: list[tuple[Url, JsonResponse, ResultData]] = [
(
OPENAI_RESPONSES_TEST_BASE_URL,
OPENAI_BASE_URL,
{
"id": "resp_fake_id_9012",
"object": "response",
@ -144,7 +268,7 @@ TOOL_CONVERSATION_PARAMS: list[tuple[Url, JsonResponse, ResultData]] = [
STREAMED_SIMPLE_CONVERSATION_PARAMS: list[tuple[Url, list[Chunk], list[ResultData]]] = [
(
OPENAI_RESPONSES_TEST_BASE_URL,
OPENAI_BASE_URL,
[
_sse_event({
"type": "response.created",
@ -235,7 +359,7 @@ STREAMED_SIMPLE_CONVERSATION_PARAMS: list[tuple[Url, list[Chunk], list[ResultDat
COMMENTARY_CONVERSATION_PARAMS: list[tuple[Url, JsonResponse, ResultData]] = [
(
OPENAI_RESPONSES_TEST_BASE_URL,
OPENAI_BASE_URL,
{
"id": "resp_thinking_1234",
"object": "response",
@ -268,7 +392,7 @@ STREAMED_COMMENTARY_CONVERSATION_PARAMS: list[
tuple[Url, list[Chunk], list[ResultData]]
] = [
(
OPENAI_RESPONSES_TEST_BASE_URL,
OPENAI_BASE_URL,
[
_sse_event({
"type": "response.created",
@ -419,7 +543,7 @@ STREAMED_COMMENTARY_CONVERSATION_PARAMS: list[
STREAMED_TOOL_CONVERSATION_PARAMS: list[tuple[Url, list[Chunk], list[ResultData]]] = [
(
OPENAI_RESPONSES_TEST_BASE_URL,
OPENAI_BASE_URL,
[
_sse_event({
"type": "response.created",