97e91a83f3
Ruff / Ruff (push) Waiting to run
Test / Core Tests (push) Waiting to run
Test / Offline Coverage Tests (Python 3.10) (push) Waiting to run
Test / Offline Coverage Tests (Python 3.11) (push) Waiting to run
Test / Offline Coverage Tests (Python 3.12) (push) Waiting to run
Test / Offline Coverage Tests (Python 3.13) (push) Waiting to run
Test / Offline Coverage Tests (Python 3.9) (push) Waiting to run
Test / Full Coverage (Python 3.11) (push) Waiting to run
Test / Core Provider Tests (OpenAI) (push) Blocked by required conditions
Test / Core Provider Tests (Anthropic) (push) Blocked by required conditions
Test / Core Provider Tests (Google) (push) Blocked by required conditions
Test / Core Provider Tests (Other) (push) Blocked by required conditions
Test / Anthropic Tests (push) Blocked by required conditions
Test / Gemini Tests (push) Blocked by required conditions
Test / Google GenAI Tests (push) Blocked by required conditions
Test / Vertex AI Tests (push) Blocked by required conditions
Test / OpenAI Tests (push) Blocked by required conditions
Test / Writer Tests (push) Blocked by required conditions
Test / Auto Client Tests (push) Blocked by required conditions
ty / type-check (push) Waiting to run
158 lines
5.8 KiB
Python
158 lines
5.8 KiB
Python
from typing_extensions import TypedDict
|
|
from pydantic import BaseModel
|
|
from instructor.processing.response import handle_response_model
|
|
from instructor.v2.core.response import _redact_kwargs
|
|
from instructor.v2.providers.bedrock.handlers import (
|
|
_prepare_bedrock_converse_kwargs_internal,
|
|
)
|
|
|
|
|
|
def test_typed_dict_conversion() -> None:
|
|
class User(TypedDict):
|
|
name: str
|
|
age: int
|
|
|
|
_, user_tool_definition = handle_response_model(User)
|
|
|
|
class User(BaseModel):
|
|
name: str
|
|
age: int
|
|
|
|
_, pydantic_user_tool_definition = handle_response_model(User)
|
|
assert user_tool_definition == pydantic_user_tool_definition
|
|
|
|
|
|
def test_redact_kwargs_hides_nested_sensitive_fields() -> None:
|
|
kwargs = {
|
|
"api_key": "top-level",
|
|
"headers": {
|
|
"Authorization": "Bearer secret",
|
|
"x-api-key": "nested secret",
|
|
"safe": "visible",
|
|
},
|
|
"messages": [{"token": "inner secret", "content": "hello"}],
|
|
}
|
|
|
|
assert _redact_kwargs(kwargs) == {
|
|
"api_key": "[redacted]",
|
|
"headers": {
|
|
"Authorization": "[redacted]",
|
|
"x-api-key": "[redacted]",
|
|
"safe": "visible",
|
|
},
|
|
"messages": [{"token": "[redacted]", "content": "hello"}],
|
|
}
|
|
|
|
|
|
def test_openai_to_bedrock_conversion() -> None:
|
|
"""OpenAI-style input should be fully converted to Bedrock format."""
|
|
call_kwargs = {
|
|
"model": "anthropic.claude-3-haiku-20240307-v1:0",
|
|
"messages": [
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{"role": "user", "content": "Extract: Jason is 22 years old"},
|
|
{"role": "assistant", "content": "Sure! Jason is 22."},
|
|
],
|
|
}
|
|
result = _prepare_bedrock_converse_kwargs_internal(call_kwargs)
|
|
assert "model" not in result
|
|
assert result["modelId"] == "anthropic.claude-3-haiku-20240307-v1:0"
|
|
assert result["system"] == [{"text": "You are a helpful assistant."}]
|
|
assert len(result["messages"]) == 2
|
|
assert result["messages"][0]["role"] == "user"
|
|
assert result["messages"][0]["content"] == [
|
|
{"text": "Extract: Jason is 22 years old"}
|
|
]
|
|
assert result["messages"][1]["role"] == "assistant"
|
|
assert result["messages"][1]["content"] == [{"text": "Sure! Jason is 22."}]
|
|
|
|
|
|
def test_bedrock_native_preserved() -> None:
|
|
"""Bedrock-native input should be preserved as-is."""
|
|
call_kwargs = {
|
|
"modelId": "anthropic.claude-3-haiku-20240307-v1:0",
|
|
"system": [{"text": "You are a helpful assistant."}],
|
|
"messages": [
|
|
{"role": "user", "content": [{"text": "Extract: Jason is 22 years old"}]},
|
|
{"role": "assistant", "content": [{"text": "Sure! Jason is 22."}]},
|
|
],
|
|
}
|
|
result = _prepare_bedrock_converse_kwargs_internal(call_kwargs)
|
|
assert result["system"] == [{"text": "You are a helpful assistant."}]
|
|
assert len(result["messages"]) == 2
|
|
assert result["messages"][0]["content"] == [
|
|
{"text": "Extract: Jason is 22 years old"}
|
|
]
|
|
assert result["messages"][1]["content"] == [{"text": "Sure! Jason is 22."}]
|
|
|
|
|
|
def test_mixed_openai_and_bedrock() -> None:
|
|
"""Mixed input: OpenAI-style is converted, Bedrock-native is preserved."""
|
|
call_kwargs = {
|
|
"modelId": "anthropic.claude-3-haiku-20240307-v1:0",
|
|
"system": [{"text": "You are a helpful assistant."}],
|
|
"messages": [
|
|
{
|
|
"role": "user",
|
|
"content": "Extract: Jason is 22 years old",
|
|
}, # OpenAI style
|
|
{
|
|
"role": "assistant",
|
|
"content": [{"text": "Sure! Jason is 22."}],
|
|
}, # Bedrock style
|
|
],
|
|
}
|
|
result = _prepare_bedrock_converse_kwargs_internal(call_kwargs)
|
|
assert result["modelId"] == "anthropic.claude-3-haiku-20240307-v1:0"
|
|
assert result["system"] == [{"text": "You are a helpful assistant."}]
|
|
assert len(result["messages"]) == 2
|
|
# OpenAI-style user message converted
|
|
assert result["modelId"] == "anthropic.claude-3-haiku-20240307-v1:0"
|
|
assert result["messages"][0]["content"] == [
|
|
{"text": "Extract: Jason is 22 years old"}
|
|
]
|
|
# Bedrock-style assistant message preserved
|
|
assert result["messages"][1]["content"] == [{"text": "Sure! Jason is 22."}]
|
|
|
|
|
|
def test_bedrock_round_trip() -> None:
|
|
"""Bedrock input should be unchanged after round-trip through the function."""
|
|
call_kwargs = {
|
|
"modelId": "anthropic.claude-3-haiku-20240307-v1:0",
|
|
"system": [{"text": "Bedrock system."}],
|
|
"messages": [
|
|
{"role": "user", "content": [{"text": "Bedrock user message."}]},
|
|
],
|
|
}
|
|
import copy
|
|
|
|
original = copy.deepcopy(call_kwargs)
|
|
result = _prepare_bedrock_converse_kwargs_internal(call_kwargs)
|
|
assert result == original
|
|
|
|
|
|
def test_empty_and_missing_content() -> None:
|
|
"""Empty messages and missing content should be handled gracefully."""
|
|
# Empty messages
|
|
call_kwargs = {"messages": []}
|
|
result = _prepare_bedrock_converse_kwargs_internal(call_kwargs)
|
|
assert result["messages"] == []
|
|
# Message with no content
|
|
call_kwargs = {"messages": [{"role": "user"}]}
|
|
result = _prepare_bedrock_converse_kwargs_internal(call_kwargs)
|
|
assert result["messages"][0]["role"] == "user"
|
|
# Should not add a content key if not present
|
|
assert "content" not in result["messages"][0]
|
|
|
|
|
|
def test_bedrock_invalid_content_format() -> None:
|
|
"""Invalid content types should raise ValueError."""
|
|
call_kwargs = {
|
|
"messages": [{"role": "user", "content": 12345}] # Invalid content type
|
|
}
|
|
try:
|
|
_prepare_bedrock_converse_kwargs_internal(call_kwargs)
|
|
raise AssertionError("Should have raised ValueError")
|
|
except ValueError as e:
|
|
assert "Unsupported message content type for Bedrock" in str(e)
|