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",

View file

@ -4,6 +4,7 @@ import json
import pytest
from tests.constants import ANTHROPIC_BASE_URL, ANTHROPIC_MESSAGES_PATH
from vibe.core.config import ProviderConfig
from vibe.core.llm.backend.anthropic import AnthropicAdapter, AnthropicMapper
from vibe.core.types import (
@ -30,7 +31,7 @@ def adapter():
def provider():
return ProviderConfig(
name="anthropic",
api_base="https://api.anthropic.com",
api_base=ANTHROPIC_BASE_URL,
api_key_env_var="ANTHROPIC_API_KEY",
api_style="anthropic",
)
@ -229,75 +230,6 @@ class TestMapperParseResponse:
assert chunk.usage.completion_tokens == 7
class TestMapperStreamingEvents:
def test_text_delta(self, mapper):
chunk, idx = mapper.parse_streaming_event(
"content_block_delta",
{"delta": {"type": "text_delta", "text": "hi"}, "index": 0},
0,
)
assert chunk.message.content == "hi"
def test_thinking_delta(self, mapper):
chunk, _ = mapper.parse_streaming_event(
"content_block_delta",
{"delta": {"type": "thinking_delta", "thinking": "hmm"}, "index": 0},
0,
)
assert chunk.message.reasoning_content == "hmm"
def test_tool_use_start(self, mapper):
chunk, idx = mapper.parse_streaming_event(
"content_block_start",
{
"content_block": {"type": "tool_use", "id": "t1", "name": "search"},
"index": 2,
},
0,
)
assert chunk.message.tool_calls[0].id == "t1"
assert idx == 2
def test_input_json_delta(self, mapper):
chunk, _ = mapper.parse_streaming_event(
"content_block_delta",
{
"delta": {"type": "input_json_delta", "partial_json": '{"q":'},
"index": 1,
},
0,
)
assert chunk.message.tool_calls[0].function.arguments == '{"q":'
def test_message_start_usage(self, mapper):
chunk, _ = mapper.parse_streaming_event(
"message_start",
{"message": {"usage": {"input_tokens": 50, "cache_read_input_tokens": 10}}},
0,
)
assert chunk.usage.prompt_tokens == 60
def test_message_delta_usage(self, mapper):
chunk, _ = mapper.parse_streaming_event(
"message_delta", {"usage": {"output_tokens": 42}}, 0
)
assert chunk.usage.completion_tokens == 42
def test_unknown_event(self, mapper):
chunk, idx = mapper.parse_streaming_event("ping", {}, 5)
assert chunk is None
assert idx == 5
def test_signature_delta(self, mapper):
chunk, _ = mapper.parse_streaming_event(
"content_block_delta",
{"delta": {"type": "signature_delta", "signature": "sig"}, "index": 0},
0,
)
assert chunk is not None
assert chunk.message.reasoning_signature == "sig"
class TestAdapterPrepareRequest:
def test_basic(self, adapter, provider):
messages = [LLMMessage(role=Role.user, content="Hello")]
@ -316,7 +248,7 @@ class TestAdapterPrepareRequest:
assert payload["model"] == "claude-sonnet-4-20250514"
assert payload["max_tokens"] == 1024
assert "temperature" not in payload
assert req.endpoint == "/v1/messages"
assert req.endpoint == ANTHROPIC_MESSAGES_PATH
assert req.headers["anthropic-version"] == "2023-06-01"
def test_beta_features(self, adapter, provider):

View file

@ -36,8 +36,9 @@ from tests.backend.data.mistral import (
STREAMED_TOOL_CONVERSATION_PARAMS as MISTRAL_STREAMED_TOOL_CONVERSATION_PARAMS,
TOOL_CONVERSATION_PARAMS as MISTRAL_TOOL_CONVERSATION_PARAMS,
)
from tests.constants import CHAT_COMPLETIONS_PATH
from vibe.core.config import ModelConfig, ProviderConfig
from vibe.core.llm.backend.factory import BACKEND_FACTORY
from vibe.core.llm.backend.factory import BACKEND_FACTORY, create_backend
from vibe.core.llm.backend.generic import GenericBackend
from vibe.core.llm.backend.mistral import MistralBackend, MistralMapper
from vibe.core.llm.exceptions import BackendError, BackendErrorBuilder
@ -71,7 +72,7 @@ class TestBackend:
self, base_url: Url, json_response: JsonResponse, result_data: ResultData
):
with respx.mock(base_url=base_url) as mock_api:
mock_api.post("/v1/chat/completions").mock(
mock_api.post(CHAT_COMPLETIONS_PATH).mock(
return_value=httpx.Response(status_code=200, json=json_response)
)
provider = ProviderConfig(
@ -139,7 +140,7 @@ class TestBackend:
self, base_url: Url, chunks: list[Chunk], result_data: list[ResultData]
):
with respx.mock(base_url=base_url) as mock_api:
mock_api.post("/v1/chat/completions").mock(
mock_api.post(CHAT_COMPLETIONS_PATH).mock(
return_value=httpx.Response(
status_code=200,
stream=httpx.ByteStream(stream=b"\n\n".join(chunks)),
@ -291,7 +292,7 @@ class TestBackend:
response: httpx.Response,
):
with respx.mock(base_url=base_url) as mock_api:
mock_api.post("/v1/chat/completions").mock(return_value=response)
mock_api.post(CHAT_COMPLETIONS_PATH).mock(return_value=response)
provider = ProviderConfig(
name="provider_name",
api_base=f"{base_url}/v1",
@ -335,7 +336,7 @@ class TestBackend:
self, base_url: Url, provider_name: str, expected_stream_options: dict
):
with respx.mock(base_url=base_url) as mock_api:
route = mock_api.post("/v1/chat/completions").mock(
route = mock_api.post(CHAT_COMPLETIONS_PATH).mock(
return_value=httpx.Response(
status_code=200,
stream=httpx.ByteStream(
@ -399,7 +400,7 @@ class TestBackend:
],
}
with respx.mock(base_url=base_url) as mock_api:
mock_api.post("/v1/chat/completions").mock(
mock_api.post(CHAT_COMPLETIONS_PATH).mock(
return_value=httpx.Response(status_code=200, json=json_response)
)
@ -441,7 +442,7 @@ class TestBackend:
stream=httpx.ByteStream(stream=b"\n\n".join(chunks)),
headers={"Content-Type": "text/event-stream"},
)
mock_api.post("/v1/chat/completions").mock(return_value=mock_response)
mock_api.post(CHAT_COMPLETIONS_PATH).mock(return_value=mock_response)
provider = ProviderConfig(
name="provider_name",
@ -470,13 +471,29 @@ class TestBackend:
class TestMistralRetry:
@staticmethod
def _create_test_backend() -> MistralBackend:
def _create_test_backend(
timeout: float = 720.0, retry_max_elapsed_time: float = 300.0
) -> MistralBackend:
provider = ProviderConfig(
name="test_provider",
api_base="https://api.mistral.ai/v1",
api_key_env_var="API_KEY",
)
return MistralBackend(provider=provider)
return MistralBackend(
provider=provider,
timeout=timeout,
retry_max_elapsed_time=retry_max_elapsed_time,
)
@staticmethod
def _build_fast_http_retry_config() -> RetryConfig:
return RetryConfig(
strategy="backoff",
backoff=BackoffStrategy(
initial_interval=1, max_interval=1, exponent=1, max_elapsed_time=10000
),
retry_connection_errors=True,
)
@pytest.mark.asyncio
async def test_client_creation_includes_timeout_and_retry_config(self):
@ -492,6 +509,61 @@ class TestMistralRetry:
assert call_kwargs["retry_config"] is backend._retry_config
assert "async_client" in call_kwargs
def test_retry_budget_uses_explicit_config(self):
backend = self._create_test_backend(
timeout=7200.0, retry_max_elapsed_time=1234.0
)
assert backend._timeout == 7200.0
assert backend._retry_config.backoff.max_elapsed_time == 1234000
def test_create_backend_passes_retry_budget(self):
provider = ProviderConfig(
name="test_provider",
api_base="https://api.mistral.ai/v1",
api_key_env_var="API_KEY",
backend=Backend.MISTRAL,
)
backend = create_backend(
provider=provider, timeout=7200.0, retry_max_elapsed_time=1234.0
)
assert isinstance(backend, MistralBackend)
assert backend._timeout == 7200.0
assert backend._retry_config.backoff.max_elapsed_time == 1234000
@pytest.mark.asyncio
async def test_complete_retries_retryable_http_error(self):
with respx.mock(base_url="https://api.mistral.ai") as mock_api:
route = mock_api.post("/v1/chat/completions").mock(
side_effect=[
httpx.Response(status_code=502, text="Bad Gateway"),
httpx.Response(
status_code=200, json=MISTRAL_SIMPLE_CONVERSATION_PARAMS[0][1]
),
]
)
backend = self._create_test_backend()
backend._retry_config = self._build_fast_http_retry_config()
model = ModelConfig(
name="model_name", provider="test_provider", alias="model_alias"
)
messages = [LLMMessage(role=Role.user, content="Just say hi")]
result = await backend.complete(
model=model,
messages=messages,
temperature=0.2,
tools=None,
max_tokens=None,
tool_choice=None,
extra_headers=None,
)
assert result.message.content == "Some content"
assert route.call_count == 2
class TestMistralMapperPrepareMessage:
"""Tests for MistralMapper.prepare_message thinking-block handling.

View file

@ -11,7 +11,7 @@ from vibe.core.llm.backend._image import (
to_base64,
to_data_uri,
)
from vibe.core.types import ImageAttachment
from vibe.core.types import FileImageSource, ImageAttachment, InlineImageSource
PNG_BYTES = b"\x89PNG\r\n\x1a\n" + b"\x00" * 16
@ -24,7 +24,9 @@ def _clear_cache() -> None:
def _att(tmp_path: Path, name: str = "shot.png") -> ImageAttachment:
p = tmp_path / name
p.write_bytes(PNG_BYTES)
return ImageAttachment(path=p, alias=name, mime_type="image/png")
return ImageAttachment(
source=FileImageSource(path=p), alias=name, mime_type="image/png"
)
def test_repeated_calls_hit_cache_and_skip_disk(tmp_path: Path) -> None:
@ -46,13 +48,14 @@ def test_cache_invalidates_when_file_mtime_changes(tmp_path: Path) -> None:
to_base64(att)
assert _encode_cached.cache_info().misses == 1
assert isinstance(att.source, FileImageSource)
new_bytes = PNG_BYTES + b"\x01"
att.path.write_bytes(new_bytes)
att.source.path.write_bytes(new_bytes)
# Force a distinct mtime even on coarse-resolution filesystems.
import os
stat = att.path.stat()
os.utime(att.path, ns=(stat.st_atime_ns, stat.st_mtime_ns + 1_000_000))
stat = att.source.path.stat()
os.utime(att.source.path, ns=(stat.st_atime_ns, stat.st_mtime_ns + 1_000_000))
refreshed = to_base64(att)
assert refreshed == base64.b64encode(new_bytes).decode("ascii")
@ -61,8 +64,23 @@ def test_cache_invalidates_when_file_mtime_changes(tmp_path: Path) -> None:
def test_missing_file_raises_image_read_error(tmp_path: Path) -> None:
att = ImageAttachment(
path=tmp_path / "nope.png", alias="nope.png", mime_type="image/png"
source=FileImageSource(path=tmp_path / "nope.png"),
alias="nope.png",
mime_type="image/png",
)
with pytest.raises(ImageReadError):
to_data_uri(att)
def test_inline_data_is_returned_without_touching_disk() -> None:
encoded = base64.b64encode(PNG_BYTES).decode("ascii")
att = ImageAttachment(
source=InlineImageSource(data=encoded),
alias="pasted.png",
mime_type="image/png",
)
assert to_base64(att) == encoded
assert to_data_uri(att) == f"data:image/png;base64,{encoded}"
assert _encode_cached.cache_info().misses == 0

View file

@ -14,7 +14,7 @@ from vibe.core.llm.backend.generic import OpenAIAdapter
from vibe.core.llm.backend.mistral import MistralMapper
from vibe.core.llm.backend.openai_responses import OpenAIResponsesAdapter
from vibe.core.llm.backend.reasoning_adapter import ReasoningAdapter
from vibe.core.types import ImageAttachment, LLMMessage, Role
from vibe.core.types import FileImageSource, ImageAttachment, LLMMessage, Role
PNG_BYTES = b"\x89PNG\r\n\x1a\n" + b"\x00" * 16
EXPECTED_B64 = base64.b64encode(PNG_BYTES).decode("ascii")
@ -25,7 +25,9 @@ EXPECTED_DATA_URI = f"data:image/png;base64,{EXPECTED_B64}"
def image_attachment(tmp_path: Path) -> ImageAttachment:
path = tmp_path / "shot.png"
path.write_bytes(PNG_BYTES)
return ImageAttachment(path=path, alias="shot.png", mime_type="image/png")
return ImageAttachment(
source=FileImageSource(path=path), alias="shot.png", mime_type="image/png"
)
def _user_message(image: ImageAttachment) -> LLMMessage:

View file

@ -19,13 +19,13 @@ import respx
from tests.backend.data import Chunk, JsonResponse, ResultData, Url
from tests.backend.data.openai_responses import (
COMMENTARY_CONVERSATION_PARAMS,
OPENAI_RESPONSES_TEST_BASE_URL,
SIMPLE_CONVERSATION_PARAMS,
STREAMED_COMMENTARY_CONVERSATION_PARAMS,
STREAMED_SIMPLE_CONVERSATION_PARAMS,
STREAMED_TOOL_CONVERSATION_PARAMS,
TOOL_CONVERSATION_PARAMS,
)
from tests.constants import OPENAI_BASE_URL, OPENAI_RESPONSES_PATH
from vibe.core.config import ModelConfig, ProviderConfig
from vibe.core.llm.backend.generic import GenericBackend
from vibe.core.llm.backend.openai_responses import OpenAIResponsesAdapter
@ -55,7 +55,7 @@ def model():
return _make_model()
def _make_provider(base_url: Url = OPENAI_RESPONSES_TEST_BASE_URL) -> ProviderConfig:
def _make_provider(base_url: Url = OPENAI_BASE_URL) -> ProviderConfig:
return ProviderConfig(
name="openai",
api_base=f"{base_url}/v1",
@ -68,7 +68,7 @@ def _make_model() -> ModelConfig:
return ModelConfig(name="gpt-4o", provider="openai", alias="gpt-4o")
def _make_backend(base_url: Url = OPENAI_RESPONSES_TEST_BASE_URL) -> GenericBackend:
def _make_backend(base_url: Url = OPENAI_BASE_URL) -> GenericBackend:
return GenericBackend(provider=_make_provider(base_url))
@ -1229,7 +1229,7 @@ class TestGenericBackendIntegration:
self, base_url: Url, json_response: JsonResponse, result_data: ResultData
):
with respx.mock(base_url=base_url) as mock_api:
mock_api.post("/v1/responses").mock(
mock_api.post(OPENAI_RESPONSES_PATH).mock(
return_value=httpx.Response(status_code=200, json=json_response)
)
backend = _make_backend(base_url)
@ -1261,7 +1261,7 @@ class TestGenericBackendIntegration:
self, base_url: Url, chunks: list[Chunk], result_data: list[ResultData]
):
with respx.mock(base_url=base_url) as mock_api:
mock_api.post("/v1/responses").mock(
mock_api.post(OPENAI_RESPONSES_PATH).mock(
return_value=httpx.Response(
status_code=200,
stream=httpx.ByteStream(stream=b"\n\n".join(chunks)),
@ -1289,9 +1289,9 @@ class TestGenericBackendIntegration:
@pytest.mark.asyncio
async def test_streaming_payload_includes_stream_flag(self):
base_url = OPENAI_RESPONSES_TEST_BASE_URL
base_url = OPENAI_BASE_URL
with respx.mock(base_url=base_url) as mock_api:
route = mock_api.post("/v1/responses").mock(
route = mock_api.post(OPENAI_RESPONSES_PATH).mock(
return_value=httpx.Response(
status_code=200,
stream=httpx.ByteStream(