MCPcopy Create free account
hub / github.com/IBM/mcp-cli / HistoryMessage

Class HistoryMessage

src/mcp_cli/chat/models.py:123–199  ·  view source on GitHub ↗

A message in the conversation history (dict-based tool_calls for SessionManager compat). This is the LOCAL message model used by ChatContext/SessionManager. For chuk_llm's canonical Message with typed ToolCall objects, use mcp_cli.chat.response_models.Message instead.

Source from the content-addressed store, hash-verified

121
122
123class HistoryMessage(BaseModel):
124 """A message in the conversation history (dict-based tool_calls for SessionManager compat).
125
126 This is the LOCAL message model used by ChatContext/SessionManager.
127 For chuk_llm's canonical Message with typed ToolCall objects,
128 use mcp_cli.chat.response_models.Message instead.
129 """
130
131 role: MessageRole = Field(
132 description="Message role (user, assistant, system, tool)"
133 )
134 content: str | list[dict[str, Any]] | None = Field(
135 default=None,
136 description="Message content (string, or list of content blocks for multimodal)",
137 )
138 name: str | None = Field(default=None, description="Name (for tool messages)")
139 tool_calls: list[dict[str, Any]] | None = Field(
140 default=None, description="Tool calls (for assistant messages with tools)"
141 )
142 tool_call_id: str | None = Field(
143 default=None, description="Tool call ID (for tool response messages)"
144 )
145 reasoning_content: str | None = Field(
146 default=None,
147 description="Reasoning content (for models like DeepSeek reasoner)",
148 )
149
150 model_config = {"frozen": False}
151
152 def to_dict(self) -> dict[str, Any]:
153 """Convert to dict for LLM API calls.
154
155 Handles provider-specific requirements:
156 - OpenAI: Requires 'content' field in assistant messages with tool_calls
157 - DeepSeek Reasoner: Requires 'reasoning_content' field when model provided it
158 """
159 result = self.model_dump(exclude_none=True, mode="json")
160
161 # CRITICAL FIX: OpenAI (especially newer models like gpt-5-mini) requires
162 # the 'content' field to be present in assistant messages with tool_calls,
163 # even if it's null. Without this, some models may hang or reject the request.
164 if self.role == MessageRole.ASSISTANT and MessageField.TOOL_CALLS in result:
165 if MessageField.CONTENT not in result:
166 result[MessageField.CONTENT] = None
167
168 # NOTE: reasoning_content is automatically included if set (not None)
169 # because we're not explicitly excluding it. The exclude_none=True will
170 # only exclude it if it's None. This is correct behavior per DeepSeek docs:
171 # https://api-docs.deepseek.com/guides/thinking_mode#tool-calls
172 # "the user needs to send the reasoning content back to the API"
173
174 return result # type: ignore[no-any-return]
175
176 @classmethod
177 def from_dict(cls, data: dict[str, Any]) -> "HistoryMessage":
178 """Create from dict."""
179 return cls.model_validate(data) # type: ignore[no-any-return]
180

Calls

no outgoing calls