v1.3.2
This commit is contained in:
parent
c79e2cf487
commit
08d8e85447
18 changed files with 287 additions and 51 deletions
|
|
@ -41,7 +41,7 @@ class TestACPInitialize:
|
|||
),
|
||||
)
|
||||
assert response.agentInfo == Implementation(
|
||||
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="1.3.1"
|
||||
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="1.3.2"
|
||||
)
|
||||
|
||||
assert response.authMethods == []
|
||||
|
|
@ -63,7 +63,7 @@ class TestACPInitialize:
|
|||
),
|
||||
)
|
||||
assert response.agentInfo == Implementation(
|
||||
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="1.3.1"
|
||||
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="1.3.2"
|
||||
)
|
||||
|
||||
assert response.authMethods is not None
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ from vibe.core.config import (
|
|||
SessionLoggingConfig,
|
||||
VibeConfig,
|
||||
)
|
||||
from vibe.core.llm.backend.generic import GenericBackend
|
||||
from vibe.core.llm.backend.mistral import MistralMapper, ParsedContent
|
||||
from vibe.core.llm.backend.generic import GenericBackend, OpenAIAdapter
|
||||
from vibe.core.llm.backend.mistral import MistralBackend, MistralMapper, ParsedContent
|
||||
from vibe.core.llm.format import APIToolFormatHandler
|
||||
from vibe.core.types import AssistantEvent, LLMMessage, ReasoningEvent, Role
|
||||
|
||||
|
|
@ -358,3 +358,175 @@ class TestLLMMessageReasoningContent:
|
|||
dumped = msg.model_dump(exclude_none=True)
|
||||
|
||||
assert "reasoning_content" not in dumped
|
||||
|
||||
|
||||
class TestReasoningFieldNameConversion:
|
||||
def test_reasoning_to_api_keeps_default_field(self):
|
||||
adapter = OpenAIAdapter()
|
||||
msg_dict = {
|
||||
"role": "assistant",
|
||||
"content": "Answer",
|
||||
"reasoning_content": "Thinking...",
|
||||
}
|
||||
|
||||
result = adapter._reasoning_to_api(msg_dict, "reasoning_content")
|
||||
|
||||
assert result["reasoning_content"] == "Thinking..."
|
||||
assert "reasoning" not in result
|
||||
|
||||
def test_reasoning_to_api_renames_to_custom_field(self):
|
||||
adapter = OpenAIAdapter()
|
||||
msg_dict = {
|
||||
"role": "assistant",
|
||||
"content": "Answer",
|
||||
"reasoning_content": "Thinking...",
|
||||
}
|
||||
|
||||
result = adapter._reasoning_to_api(msg_dict, "reasoning")
|
||||
|
||||
assert result["reasoning"] == "Thinking..."
|
||||
assert "reasoning_content" not in result
|
||||
|
||||
def test_reasoning_from_api_converts_custom_field(self):
|
||||
adapter = OpenAIAdapter()
|
||||
msg_dict = {
|
||||
"role": "assistant",
|
||||
"content": "Answer",
|
||||
"reasoning": "Thinking...",
|
||||
}
|
||||
|
||||
result = adapter._reasoning_from_api(msg_dict, "reasoning")
|
||||
|
||||
assert result["reasoning_content"] == "Thinking..."
|
||||
assert "reasoning" not in result
|
||||
|
||||
def test_reasoning_from_api_keeps_default_field(self):
|
||||
adapter = OpenAIAdapter()
|
||||
msg_dict = {
|
||||
"role": "assistant",
|
||||
"content": "Answer",
|
||||
"reasoning_content": "Thinking...",
|
||||
}
|
||||
|
||||
result = adapter._reasoning_from_api(msg_dict, "reasoning_content")
|
||||
|
||||
assert result["reasoning_content"] == "Thinking..."
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_complete_with_custom_reasoning_field_name(self):
|
||||
base_url = "https://api.example.com"
|
||||
json_response = {
|
||||
"id": "fake_id",
|
||||
"created": 1234567890,
|
||||
"model": "test-model",
|
||||
"usage": {"prompt_tokens": 10, "completion_tokens": 20, "total_tokens": 30},
|
||||
"object": "chat.completion",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"finish_reason": "stop",
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "The answer is 42.",
|
||||
"reasoning": "Let me think step by step...",
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
with respx.mock(base_url=base_url) as mock_api:
|
||||
mock_api.post("/v1/chat/completions").mock(
|
||||
return_value=httpx.Response(status_code=200, json=json_response)
|
||||
)
|
||||
provider = ProviderConfig(
|
||||
name="test",
|
||||
api_base=f"{base_url}/v1",
|
||||
api_key_env_var="API_KEY",
|
||||
reasoning_field_name="reasoning",
|
||||
)
|
||||
backend = GenericBackend(provider=provider)
|
||||
model = ModelConfig(name="test-model", provider="test", alias="test")
|
||||
messages = [LLMMessage(role=Role.user, content="What is the answer?")]
|
||||
|
||||
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 == "The answer is 42."
|
||||
assert result.message.reasoning_content == "Let me think step by step..."
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_streaming_with_custom_reasoning_field_name(self):
|
||||
base_url = "https://api.example.com"
|
||||
chunks = [
|
||||
b'data: {"id":"id1","object":"chat.completion.chunk","created":123,"model":"test","choices":[{"index":0,"delta":{"role":"assistant","reasoning":"Thinking..."},"finish_reason":null}]}',
|
||||
b'data: {"id":"id1","object":"chat.completion.chunk","created":123,"model":"test","choices":[{"index":0,"delta":{"content":"Answer"},"finish_reason":null}]}',
|
||||
b'data: {"id":"id1","object":"chat.completion.chunk","created":123,"model":"test","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":10,"completion_tokens":5}}',
|
||||
b"data: [DONE]",
|
||||
]
|
||||
|
||||
with respx.mock(base_url=base_url) as mock_api:
|
||||
mock_api.post("/v1/chat/completions").mock(
|
||||
return_value=httpx.Response(
|
||||
status_code=200,
|
||||
stream=httpx.ByteStream(stream=b"\n\n".join(chunks)),
|
||||
headers={"Content-Type": "text/event-stream"},
|
||||
)
|
||||
)
|
||||
provider = ProviderConfig(
|
||||
name="test",
|
||||
api_base=f"{base_url}/v1",
|
||||
api_key_env_var="API_KEY",
|
||||
reasoning_field_name="reasoning",
|
||||
)
|
||||
backend = GenericBackend(provider=provider)
|
||||
model = ModelConfig(name="test-model", provider="test", alias="test")
|
||||
messages = [LLMMessage(role=Role.user, content="Stream please")]
|
||||
|
||||
results = []
|
||||
async for chunk in backend.complete_streaming(
|
||||
model=model,
|
||||
messages=messages,
|
||||
temperature=0.2,
|
||||
tools=None,
|
||||
max_tokens=None,
|
||||
tool_choice=None,
|
||||
extra_headers=None,
|
||||
):
|
||||
results.append(chunk)
|
||||
|
||||
assert results[0].message.reasoning_content == "Thinking..."
|
||||
assert results[1].message.content == "Answer"
|
||||
|
||||
|
||||
class TestMistralReasoningFieldNameValidation:
|
||||
def test_mistral_backend_rejects_custom_reasoning_field_name(self):
|
||||
provider = ProviderConfig(
|
||||
name="mistral",
|
||||
api_base="https://api.mistral.ai/v1",
|
||||
api_key_env_var="MISTRAL_API_KEY",
|
||||
reasoning_field_name="reasoning",
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError) as exc_info:
|
||||
MistralBackend(provider=provider)
|
||||
|
||||
assert "does not support custom reasoning_field_name" in str(exc_info.value)
|
||||
assert "reasoning" in str(exc_info.value)
|
||||
|
||||
def test_mistral_backend_accepts_default_reasoning_field_name(self):
|
||||
provider = ProviderConfig(
|
||||
name="mistral",
|
||||
api_base="https://api.mistral.ai/v1",
|
||||
api_key_env_var="MISTRAL_API_KEY",
|
||||
reasoning_field_name="reasoning_content",
|
||||
)
|
||||
|
||||
backend = MistralBackend(provider=provider)
|
||||
assert backend is not None
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue