195 lines
7.2 KiB
Python
195 lines
7.2 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
"""
|
|
Tests for structured output integration with server.
|
|
|
|
These tests cover the _inject_json_instruction function from server.py
|
|
which is used for injecting JSON schema instructions into messages.
|
|
"""
|
|
|
|
from omlx.server import _inject_json_instruction
|
|
|
|
|
|
class TestInjectJsonInstruction:
|
|
"""Tests for _inject_json_instruction function in server."""
|
|
|
|
def test_inject_new_system_message(self):
|
|
"""Test injecting instruction when no system message exists."""
|
|
messages = [{"role": "user", "content": "Hello"}]
|
|
result = _inject_json_instruction(messages, "Return JSON only")
|
|
|
|
assert len(result) == 2
|
|
assert result[0]["role"] == "system"
|
|
assert "Return JSON only" in result[0]["content"]
|
|
assert result[1]["role"] == "user"
|
|
|
|
def test_append_to_existing_system(self):
|
|
"""Test appending to existing system message."""
|
|
messages = [
|
|
{"role": "system", "content": "You are helpful."},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
result = _inject_json_instruction(messages, "Return JSON only")
|
|
|
|
assert len(result) == 2
|
|
assert result[0]["role"] == "system"
|
|
assert "You are helpful." in result[0]["content"]
|
|
assert "Return JSON only" in result[0]["content"]
|
|
|
|
def test_does_not_modify_original(self):
|
|
"""Test that original messages are not modified."""
|
|
original = [{"role": "user", "content": "Hello"}]
|
|
original_content = original[0]["content"]
|
|
result = _inject_json_instruction(original, "Return JSON only")
|
|
|
|
# Original should be unchanged
|
|
assert len(original) == 1
|
|
assert original[0]["content"] == original_content
|
|
# Result should have 2 messages
|
|
assert len(result) == 2
|
|
|
|
def test_inject_with_multiple_user_messages(self):
|
|
"""Test injection with multiple user messages."""
|
|
messages = [
|
|
{"role": "user", "content": "First message"},
|
|
{"role": "assistant", "content": "Response"},
|
|
{"role": "user", "content": "Second message"},
|
|
]
|
|
result = _inject_json_instruction(messages, "Use JSON format")
|
|
|
|
assert len(result) == 4 # System added
|
|
assert result[0]["role"] == "system"
|
|
assert "Use JSON format" in result[0]["content"]
|
|
|
|
def test_inject_with_system_in_middle(self):
|
|
"""Test that mid-conversation system messages are not mutated."""
|
|
messages = [
|
|
{"role": "user", "content": "Hi"},
|
|
{"role": "system", "content": "System prompt"},
|
|
{"role": "user", "content": "Question"},
|
|
]
|
|
result = _inject_json_instruction(messages, "JSON only")
|
|
|
|
assert len(result) == 4
|
|
assert result[0]["role"] == "system"
|
|
assert "JSON only" in result[0]["content"]
|
|
assert result[2]["role"] == "system"
|
|
assert result[2]["content"] == "System prompt"
|
|
|
|
def test_inject_empty_instruction(self):
|
|
"""Test injection with empty instruction."""
|
|
messages = [{"role": "user", "content": "Hello"}]
|
|
result = _inject_json_instruction(messages, "")
|
|
|
|
assert len(result) == 2
|
|
assert result[0]["role"] == "system"
|
|
# Content should still be set (may just be newlines)
|
|
assert result[0]["content"] is not None
|
|
|
|
def test_inject_multiline_instruction(self):
|
|
"""Test injection with multiline instruction."""
|
|
instruction = """Please respond with JSON.
|
|
The JSON should have these fields:
|
|
- name: string
|
|
- age: integer"""
|
|
messages = [{"role": "user", "content": "Hello"}]
|
|
result = _inject_json_instruction(messages, instruction)
|
|
|
|
assert "name: string" in result[0]["content"]
|
|
assert "age: integer" in result[0]["content"]
|
|
|
|
def test_inject_preserves_message_order(self):
|
|
"""Test that message order is preserved."""
|
|
messages = [
|
|
{"role": "system", "content": "You are helpful."},
|
|
{"role": "user", "content": "First"},
|
|
{"role": "assistant", "content": "Response 1"},
|
|
{"role": "user", "content": "Second"},
|
|
]
|
|
result = _inject_json_instruction(messages, "JSON please")
|
|
|
|
assert len(result) == 4
|
|
assert result[0]["role"] == "system"
|
|
assert result[1]["role"] == "user"
|
|
assert result[1]["content"] == "First"
|
|
assert result[2]["role"] == "assistant"
|
|
assert result[3]["role"] == "user"
|
|
assert result[3]["content"] == "Second"
|
|
|
|
def test_inject_with_pydantic_style_message(self):
|
|
"""Test injection with message objects that have attributes."""
|
|
|
|
class MockMessage:
|
|
def __init__(self, role, content):
|
|
self.role = role
|
|
self.content = content
|
|
|
|
messages = [MockMessage("system", "Base instructions")]
|
|
result = _inject_json_instruction(messages, "Add JSON")
|
|
|
|
assert len(result) == 1
|
|
# The modified message should have updated content
|
|
assert "Add JSON" in result[0].content
|
|
assert "Base instructions" in result[0].content
|
|
|
|
def test_inject_with_dict_and_pydantic_mixed(self):
|
|
"""Test injection handles mixed dict and pydantic-style messages."""
|
|
|
|
class MockMessage:
|
|
def __init__(self, role, content):
|
|
self.role = role
|
|
self.content = content
|
|
|
|
messages = [
|
|
{"role": "user", "content": "Hello"},
|
|
MockMessage("assistant", "Response"),
|
|
]
|
|
result = _inject_json_instruction(messages, "JSON format")
|
|
|
|
assert len(result) == 3
|
|
assert result[0]["role"] == "system"
|
|
|
|
|
|
class TestInjectJsonInstructionEdgeCases:
|
|
"""Edge case tests for _inject_json_instruction."""
|
|
|
|
def test_empty_messages_list(self):
|
|
"""Test with empty messages list."""
|
|
messages = []
|
|
result = _inject_json_instruction(messages, "JSON instruction")
|
|
|
|
assert len(result) == 1
|
|
assert result[0]["role"] == "system"
|
|
assert "JSON instruction" in result[0]["content"]
|
|
|
|
def test_system_message_with_none_content(self):
|
|
"""Test handling system message with None content."""
|
|
|
|
class MockMessage:
|
|
def __init__(self):
|
|
self.role = "system"
|
|
self.content = None
|
|
|
|
messages = [MockMessage()]
|
|
result = _inject_json_instruction(messages, "Add this")
|
|
|
|
# Should handle None content gracefully
|
|
assert len(result) == 1
|
|
# Content should now include the instruction
|
|
assert "Add this" in result[0].content
|
|
|
|
def test_special_characters_in_instruction(self):
|
|
"""Test instruction with special characters."""
|
|
instruction = '{"type": "object", "properties": {"name": {"type": "string"}}}'
|
|
messages = [{"role": "user", "content": "Hello"}]
|
|
result = _inject_json_instruction(messages, instruction)
|
|
|
|
assert '{"type": "object"' in result[0]["content"]
|
|
|
|
def test_unicode_in_instruction(self):
|
|
"""Test instruction with unicode characters."""
|
|
instruction = "Return JSON with Chinese: \u4e2d\u6587"
|
|
messages = [{"role": "user", "content": "Hello"}]
|
|
result = _inject_json_instruction(messages, instruction)
|
|
|
|
assert "\u4e2d\u6587" in result[0]["content"]
|