51 lines
1.6 KiB
Python
51 lines
1.6 KiB
Python
"""Message schema for LLM conversation history."""
|
|
|
|
from typing import Any, Literal
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
from app.models.tool_call import ToolCall
|
|
|
|
|
|
class Message(BaseModel):
|
|
"""A single message in the conversation history.
|
|
|
|
Follows the OpenAI chat completions message format with support for
|
|
system, user, assistant, and tool roles.
|
|
"""
|
|
|
|
role: Literal["system", "user", "assistant", "tool"] = Field(
|
|
description="Role of the message sender"
|
|
)
|
|
content: str | None = Field(default=None, description="Text content of the message")
|
|
tool_calls: list[ToolCall] | None = Field(
|
|
default=None, description="Tool calls made by the assistant"
|
|
)
|
|
tool_call_id: str | None = Field(
|
|
default=None, description="ID of the tool call this message responds to (role=tool)"
|
|
)
|
|
name: str | None = Field(
|
|
default=None, description="Name of the tool that produced this message (role=tool)"
|
|
)
|
|
|
|
def to_api_dict(self) -> dict[str, Any]:
|
|
"""Serialize to a dict suitable for the OpenAI-compatible API.
|
|
|
|
Strips None-valued fields to keep the payload clean.
|
|
"""
|
|
data: dict[str, Any] = {"role": self.role}
|
|
|
|
if self.content is not None:
|
|
data["content"] = self.content
|
|
|
|
if self.tool_calls is not None:
|
|
data["tool_calls"] = [tc.model_dump() for tc in self.tool_calls]
|
|
|
|
if self.tool_call_id is not None:
|
|
data["tool_call_id"] = self.tool_call_id
|
|
|
|
if self.name is not None:
|
|
data["name"] = self.name
|
|
|
|
return data
|