| | import json |
| | from typing import Literal |
| |
|
| | import toons |
| | from langchain_core.messages import ( |
| | AIMessage, |
| | BaseMessage, |
| | HumanMessage, |
| | ToolMessage, |
| | ) |
| | from langchain_core.messages import ( |
| | ChatMessage as LangchainChatMessage, |
| | ) |
| |
|
| | from schema import ChatMessage, ChatMessagePreview |
| |
|
| |
|
| | def convert_tool_response_to_toon(content: str) -> str: |
| | """ |
| | Convert JSON tool response to TOON format to reduce tokens (30–60% fewer than JSON). |
| | https://pypi.org/project/toons/ |
| | """ |
| | if not content or not isinstance(content, str): |
| | return content |
| | try: |
| | data = json.loads(content) |
| | except (json.JSONDecodeError, TypeError): |
| | return content |
| | return toons.dumps(data) |
| |
|
| | def convert_message_content_to_string(content: str | list[str | dict]) -> str: |
| | if isinstance(content, str): |
| | return content |
| | text: list[str] = [] |
| | for content_item in content: |
| | if isinstance(content_item, str): |
| | text.append(content_item) |
| | continue |
| | if content_item["type"] == "text": |
| | text.append(content_item["text"]) |
| | return "".join(text) |
| |
|
| |
|
| | def langchain_to_chat_message(message: BaseMessage) -> ChatMessage: |
| | """Create a ChatMessage from a LangChain message.""" |
| | match message: |
| | case HumanMessage(): |
| | human_message = ChatMessage( |
| | type="human", |
| | content=convert_message_content_to_string(message.content), |
| | ) |
| | return human_message |
| | case AIMessage(): |
| | ai_message = ChatMessage( |
| | type="ai", |
| | content=convert_message_content_to_string(message.content), |
| | ) |
| | if message.tool_calls: |
| | ai_message.tool_calls = message.tool_calls |
| | if message.response_metadata: |
| | ai_message.response_metadata = message.response_metadata |
| | return ai_message |
| | case ToolMessage(): |
| | tool_message = ChatMessage( |
| | type="tool", |
| | content=convert_message_content_to_string(message.content), |
| | tool_call_id=message.tool_call_id, |
| | name=message.name, |
| | ) |
| | return tool_message |
| | case LangchainChatMessage(): |
| | if message.role == "custom": |
| | custom_message = ChatMessage( |
| | type="custom", |
| | content="", |
| | custom_data=message.content[0], |
| | ) |
| | return custom_message |
| | else: |
| | raise ValueError(f"Unsupported chat message role: {message.role}") |
| | case _: |
| | raise ValueError(f"Unsupported message type: {message.__class__.__name__}") |
| |
|
| |
|
| | def message_to_preview( |
| | message: BaseMessage, |
| | index: int, |
| | content_max: int = 200, |
| | ) -> ChatMessagePreview: |
| | """Build a minimal preview DTO from a LangChain message.""" |
| | content = convert_message_content_to_string(message.content) |
| | if len(content) > content_max: |
| | content = content[: content_max].rstrip() + "…" |
| | msg_type: Literal["human", "ai", "tool", "custom"] |
| | if isinstance(message, HumanMessage): |
| | msg_type = "human" |
| | elif isinstance(message, AIMessage): |
| | msg_type = "ai" |
| | elif isinstance(message, ToolMessage): |
| | msg_type = "tool" |
| | elif isinstance(message, LangchainChatMessage) and getattr(message, "role", None) == "custom": |
| | msg_type = "custom" |
| | else: |
| | msg_type = "human" |
| | return ChatMessagePreview( |
| | type=msg_type, |
| | content=content, |
| | id=str(index), |
| | ) |
| |
|
| |
|
| | def remove_tool_calls(content: str | list[str | dict]) -> str | list[str | dict]: |
| | """Remove tool calls from content.""" |
| | if isinstance(content, str): |
| | return content |
| | |
| | return [ |
| | content_item |
| | for content_item in content |
| | if isinstance(content_item, str) or content_item["type"] != "tool_use" |
| | ] |
| |
|