mirror of
https://github.com/NCBM/plyngent.git
synced 2026-11-07 15:37:55 +08:00
91 lines
3.2 KiB
Python
91 lines
3.2 KiB
Python
|
|
"""Compatibility normalization for chat-completions wire requests."""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from collections.abc import Sequence
|
||
|
|
|
||
|
|
import msgspec
|
||
|
|
|
||
|
|
from plyngent.lmproto.deepseek.openai_compat.model import ChatCompletionsParam as DeepseekChatCompletionsParam
|
||
|
|
from plyngent.lmproto.openai_compatible.compat import (
|
||
|
|
coerce_chat_completions_param,
|
||
|
|
coerce_chat_completions_param_any,
|
||
|
|
coerce_developer_messages_to_system,
|
||
|
|
developer_to_system_message,
|
||
|
|
)
|
||
|
|
from plyngent.lmproto.openai_compatible.model import (
|
||
|
|
AnyChatMessage,
|
||
|
|
AssistantChatMessage,
|
||
|
|
ChatCompletionsParam,
|
||
|
|
DeveloperChatMessage,
|
||
|
|
SystemChatMessage,
|
||
|
|
UserChatMessage,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def _roles(messages: Sequence[object]) -> list[str]:
|
||
|
|
raw = msgspec.json.decode(msgspec.json.encode(messages))
|
||
|
|
return [str(item["role"]) for item in raw]
|
||
|
|
|
||
|
|
|
||
|
|
def test_developer_to_system_message_preserves_content_and_name() -> None:
|
||
|
|
msg = DeveloperChatMessage(content="follow these rules", name="policy")
|
||
|
|
converted = developer_to_system_message(msg)
|
||
|
|
assert isinstance(converted, SystemChatMessage)
|
||
|
|
assert converted.content == "follow these rules"
|
||
|
|
assert converted.name == "policy"
|
||
|
|
|
||
|
|
|
||
|
|
def test_coerce_developer_messages_to_system() -> None:
|
||
|
|
messages: list[AnyChatMessage] = [
|
||
|
|
SystemChatMessage(content="system"),
|
||
|
|
DeveloperChatMessage(content="dev"),
|
||
|
|
UserChatMessage(content="hi"),
|
||
|
|
AssistantChatMessage(content="hello"),
|
||
|
|
]
|
||
|
|
coerced = coerce_developer_messages_to_system(messages)
|
||
|
|
assert coerced is not messages
|
||
|
|
assert _roles(coerced) == ["system", "system", "user", "assistant"]
|
||
|
|
assert coerced[1].content == "dev"
|
||
|
|
|
||
|
|
|
||
|
|
def test_coerce_returns_equal_list_when_no_developer() -> None:
|
||
|
|
messages: list[AnyChatMessage] = [SystemChatMessage(content="system"), UserChatMessage(content="hi")]
|
||
|
|
assert coerce_developer_messages_to_system(messages) == messages
|
||
|
|
|
||
|
|
|
||
|
|
def test_coerce_chat_param_encodes_without_developer_role() -> None:
|
||
|
|
param = ChatCompletionsParam(
|
||
|
|
model="model",
|
||
|
|
messages=[
|
||
|
|
DeveloperChatMessage(content="dev"),
|
||
|
|
UserChatMessage(content="hi"),
|
||
|
|
],
|
||
|
|
)
|
||
|
|
coerced = coerce_chat_completions_param(param)
|
||
|
|
raw = msgspec.json.decode(msgspec.json.encode(coerced))
|
||
|
|
assert raw["messages"][0]["role"] == "system"
|
||
|
|
assert raw["messages"][1]["role"] == "user"
|
||
|
|
|
||
|
|
|
||
|
|
def test_deepseek_param_accepts_coerced_messages() -> None:
|
||
|
|
base = ChatCompletionsParam(
|
||
|
|
model="deepseek",
|
||
|
|
messages=[DeveloperChatMessage(content="dev"), UserChatMessage(content="hi")],
|
||
|
|
)
|
||
|
|
coerced = coerce_chat_completions_param(base)
|
||
|
|
raw = msgspec.json.decode(msgspec.json.encode(coerced))
|
||
|
|
deepseek_param = msgspec.convert(raw, DeepseekChatCompletionsParam)
|
||
|
|
assert _roles(deepseek_param.messages) == ["system", "user"]
|
||
|
|
|
||
|
|
|
||
|
|
def test_generic_coercion_handles_deepseek_runtime_param() -> None:
|
||
|
|
base = ChatCompletionsParam(
|
||
|
|
model="deepseek",
|
||
|
|
messages=[DeveloperChatMessage(content="dev"), UserChatMessage(content="hi")],
|
||
|
|
)
|
||
|
|
# Runtime calls may pass a generic param through a DeepSeek-typed method.
|
||
|
|
coerced = coerce_chat_completions_param_any(base)
|
||
|
|
raw = msgspec.json.decode(msgspec.json.encode(coerced))
|
||
|
|
assert raw["messages"][0]["role"] == "system"
|