33import sys
44from functools import singledispatch
55
6- from htmltools import HTML , Tagifiable
6+ from htmltools import HTML , Tagifiable , TagList
77
8+ from ._chat_normalize_chatlas import tool_request_contents , tool_result_contents
89from ._chat_types import ChatMessage
910
1011__all__ = ["message_content" , "message_content_chunk" ]
1112
1213
1314@singledispatch
14- def message_content (message ) -> ChatMessage :
15+ def message_content (message ):
1516 """
1617 Extract content from various message types into a ChatMessage.
1718
@@ -42,7 +43,7 @@ def message_content(message) -> ChatMessage:
4243 If the message type is unsupported.
4344 """
4445 if isinstance (message , (str , HTML )) or message is None :
45- return ChatMessage (content = message , role = "assistant" )
46+ return ChatMessage (content = message )
4647 if isinstance (message , dict ):
4748 if "content" not in message :
4849 raise ValueError ("Message dictionary must have a 'content' key" )
@@ -57,7 +58,7 @@ def message_content(message) -> ChatMessage:
5758
5859
5960@singledispatch
60- def message_content_chunk (chunk ) -> ChatMessage :
61+ def message_content_chunk (chunk ):
6162 """
6263 Extract content from various message chunk types into a ChatMessage.
6364
@@ -88,7 +89,7 @@ def message_content_chunk(chunk) -> ChatMessage:
8889 If the chunk type is unsupported.
8990 """
9091 if isinstance (chunk , (str , HTML )) or chunk is None :
91- return ChatMessage (content = chunk , role = "assistant" )
92+ return ChatMessage (content = chunk )
9293 if isinstance (chunk , dict ):
9394 if "content" not in chunk :
9495 raise ValueError ("Chunk dictionary must have a 'content' key" )
@@ -108,15 +109,71 @@ def message_content_chunk(chunk) -> ChatMessage:
108109
109110
110111@message_content .register
111- def _ (message : Tagifiable ) -> ChatMessage :
112- return ChatMessage (content = message , role = "assistant" )
112+ def _ (message : Tagifiable ):
113+ return ChatMessage (content = message )
113114
114115
115116@message_content_chunk .register
116- def _ (chunk : Tagifiable ) -> ChatMessage :
117- return ChatMessage (content = chunk , role = "assistant" )
117+ def _ (chunk : Tagifiable ):
118+ return ChatMessage (content = chunk )
118119
119120
121+ # -----------------------------------------------------------------
122+ # chatlas tool call display
123+ # -----------------------------------------------------------------
124+ try :
125+ from chatlas import ContentToolRequest , ContentToolResult , Turn
126+ from chatlas .types import Content , ContentText
127+
128+ @message_content .register
129+ def _ (message : Content ):
130+ return ChatMessage (content = str (message ))
131+
132+ @message_content_chunk .register
133+ def _ (chunk : Content ):
134+ return message_content (chunk )
135+
136+ @message_content .register
137+ def _ (message : ContentText ):
138+ return ChatMessage (content = message .text )
139+
140+ @message_content_chunk .register
141+ def _ (chunk : ContentText ):
142+ return message_content (chunk )
143+
144+ @message_content .register
145+ def _ (chunk : ContentToolRequest ):
146+ return ChatMessage (content = tool_request_contents (chunk ))
147+
148+ @message_content_chunk .register
149+ def _ (chunk : ContentToolRequest ):
150+ return message_content (chunk )
151+
152+ @message_content .register
153+ def _ (chunk : ContentToolResult ):
154+ return ChatMessage (content = tool_result_contents (chunk ))
155+
156+ @message_content_chunk .register
157+ def _ (chunk : ContentToolResult ):
158+ return message_content (chunk )
159+
160+ @message_content .register
161+ def _ (message : Turn ):
162+ contents = TagList ()
163+ for x in message .contents :
164+ contents .append (message_content (x ).content )
165+ return ChatMessage (content = contents )
166+
167+ @message_content_chunk .register
168+ def _ (chunk : Turn ):
169+ return message_content (chunk )
170+
171+ # N.B., unlike R, Python Chat stores UI state and so can replay
172+ # it with additional workarounds. That's why R currently has a
173+ # shinychat_contents() method for Chat, but Python doesn't.
174+ except ImportError :
175+ pass
176+
120177# ------------------------------------------------------------------
121178# LangChain content extractor
122179# ------------------------------------------------------------------
@@ -125,7 +182,7 @@ def _(chunk: Tagifiable) -> ChatMessage:
125182 from langchain_core .messages import BaseMessage , BaseMessageChunk
126183
127184 @message_content .register
128- def _ (message : BaseMessage ) -> ChatMessage :
185+ def _ (message : BaseMessage ):
129186 if isinstance (message .content , list ):
130187 raise ValueError (
131188 "The `message.content` provided seems to represent numerous messages. "
@@ -137,7 +194,7 @@ def _(message: BaseMessage) -> ChatMessage:
137194 )
138195
139196 @message_content_chunk .register
140- def _ (chunk : BaseMessageChunk ) -> ChatMessage :
197+ def _ (chunk : BaseMessageChunk ):
141198 if isinstance (chunk .content , list ):
142199 raise ValueError (
143200 "The `chunk.content` provided seems to represent numerous message chunks. "
@@ -159,14 +216,14 @@ def _(chunk: BaseMessageChunk) -> ChatMessage:
159216 from openai .types .chat import ChatCompletion , ChatCompletionChunk
160217
161218 @message_content .register
162- def _ (message : ChatCompletion ) -> ChatMessage :
219+ def _ (message : ChatCompletion ):
163220 return ChatMessage (
164221 content = message .choices [0 ].message .content ,
165222 role = "assistant" ,
166223 )
167224
168225 @message_content_chunk .register
169- def _ (chunk : ChatCompletionChunk ) -> ChatMessage :
226+ def _ (chunk : ChatCompletionChunk ):
170227 return ChatMessage (
171228 content = chunk .choices [0 ].delta .content ,
172229 role = "assistant" ,
@@ -185,21 +242,23 @@ def _(chunk: ChatCompletionChunk) -> ChatMessage:
185242 )
186243
187244 @message_content .register
188- def _ (message : AnthropicMessage ) -> ChatMessage :
245+ def _ (message : AnthropicMessage ):
189246 content = message .content [0 ]
190247 if content .type != "text" :
191248 raise ValueError (
192249 f"Anthropic message type { content .type } not supported. "
193250 "Only 'text' type is currently supported"
194251 )
195- return ChatMessage (content = content .text , role = "assistant" )
252+ return ChatMessage (content = content .text )
196253
197254 # Old versions of singledispatch doesn't seem to support union types
198255 if sys .version_info >= (3 , 11 ):
199- from anthropic .types import RawMessageStreamEvent
256+ from anthropic .types import ( # pyright: ignore[reportMissingImports]
257+ RawMessageStreamEvent ,
258+ )
200259
201260 @message_content_chunk .register
202- def _ (chunk : RawMessageStreamEvent ) -> ChatMessage :
261+ def _ (chunk : RawMessageStreamEvent ):
203262 content = ""
204263 if chunk .type == "content_block_delta" :
205264 if chunk .delta .type != "text_delta" :
@@ -209,7 +268,7 @@ def _(chunk: RawMessageStreamEvent) -> ChatMessage:
209268 )
210269 content = chunk .delta .text
211270
212- return ChatMessage (content = content , role = "assistant" )
271+ return ChatMessage (content = content )
213272except ImportError :
214273 pass
215274
@@ -224,12 +283,12 @@ def _(chunk: RawMessageStreamEvent) -> ChatMessage:
224283 )
225284
226285 @message_content .register
227- def _ (message : GenerateContentResponse ) -> ChatMessage :
228- return ChatMessage (content = message .text , role = "assistant" )
286+ def _ (message : GenerateContentResponse ):
287+ return ChatMessage (content = message .text )
229288
230289 @message_content_chunk .register
231- def _ (chunk : GenerateContentResponse ) -> ChatMessage :
232- return ChatMessage (content = chunk .text , role = "assistant" )
290+ def _ (chunk : GenerateContentResponse ):
291+ return ChatMessage (content = chunk .text )
233292
234293except ImportError :
235294 pass
@@ -243,14 +302,14 @@ def _(chunk: GenerateContentResponse) -> ChatMessage:
243302 from ollama import ChatResponse
244303
245304 @message_content .register
246- def _ (message : ChatResponse ) -> ChatMessage :
305+ def _ (message : ChatResponse ):
247306 msg = message .message
248- return ChatMessage (msg .content , role = "assistant" )
307+ return ChatMessage (msg .content )
249308
250309 @message_content_chunk .register
251- def _ (chunk : ChatResponse ) -> ChatMessage :
310+ def _ (chunk : ChatResponse ):
252311 msg = chunk .message
253- return ChatMessage (msg .content , role = "assistant" )
312+ return ChatMessage (msg .content )
254313
255314except ImportError :
256315 pass
0 commit comments