Process a Message into Gemini API format using the PascalCase technical protocol. Extracts text, handles files, and appends ToolCalls/ToolResults blocks.
(
message: Message, tempdir: Path | None = None, tagged: bool = True, wrap_tool: bool = True
)
| 68 | |
| 69 | @staticmethod |
| 70 | async def process_message( |
| 71 | message: Message, tempdir: Path | None = None, tagged: bool = True, wrap_tool: bool = True |
| 72 | ) -> tuple[str, list[Path | str]]: |
| 73 | """ |
| 74 | Process a Message into Gemini API format using the PascalCase technical protocol. |
| 75 | Extracts text, handles files, and appends ToolCalls/ToolResults blocks. |
| 76 | """ |
| 77 | files: list[Path | str] = [] |
| 78 | text_fragments: list[str] = [] |
| 79 | |
| 80 | if isinstance(message.content, str): |
| 81 | if message.content or message.role == "tool": |
| 82 | text_fragments.append(message.content or "") |
| 83 | elif isinstance(message.content, list): |
| 84 | for item in message.content: |
| 85 | if item.type == "text": |
| 86 | if item.text or message.role == "tool": |
| 87 | text_fragments.append(item.text or "") |
| 88 | elif item.type == "image_url": |
| 89 | if not item.image_url: |
| 90 | raise ValueError("Image URL cannot be empty") |
| 91 | if url := item.image_url.get("url", None): |
| 92 | files.append(await save_url_to_tempfile(url, tempdir)) |
| 93 | else: |
| 94 | raise ValueError("Image URL must contain 'url' key") |
| 95 | elif item.type == "file": |
| 96 | if not item.file: |
| 97 | raise ValueError("File cannot be empty") |
| 98 | if file_data := item.file.get("file_data", None): |
| 99 | filename = item.file.get("filename", "") |
| 100 | files.append(await save_file_to_tempfile(file_data, filename, tempdir)) |
| 101 | elif url := item.file.get("url", None): |
| 102 | files.append(await save_url_to_tempfile(url, tempdir)) |
| 103 | else: |
| 104 | raise ValueError("File must contain 'file_data' or 'url' key") |
| 105 | elif message.content is None and message.role == "tool": |
| 106 | text_fragments.append("") |
| 107 | elif message.content is not None: |
| 108 | raise ValueError("Unsupported message content type.") |
| 109 | |
| 110 | if message.role == "tool": |
| 111 | tool_name = message.name or "unknown" |
| 112 | combined_content = "\n".join(text_fragments).strip() |
| 113 | res_block = ( |
| 114 | f"[Result:{tool_name}]\n[ToolResult]\n{combined_content}\n[/ToolResult]\n[/Result]" |
| 115 | ) |
| 116 | if wrap_tool: |
| 117 | text_fragments = [f"[ToolResults]\n{res_block}\n[/ToolResults]"] |
| 118 | else: |
| 119 | text_fragments = [res_block] |
| 120 | |
| 121 | if message.tool_calls: |
| 122 | tool_blocks: list[str] = [] |
| 123 | for call in message.tool_calls: |
| 124 | params_text = call.function.arguments.strip() |
| 125 | formatted_params = "" |
| 126 | if params_text: |
| 127 | try: |
no test coverage detected