|
143 | 143 | "before a response was recorded)." |
144 | 144 | ) |
145 | 145 |
|
| 146 | +_LITELLM_THOUGHT_SIGNATURE_SEPARATOR = "__thought__" |
| 147 | + |
| 148 | + |
| 149 | +def _decode_litellm_tool_call_id( |
| 150 | + tool_call_id: str, |
| 151 | +) -> tuple[str, Optional[bytes]]: |
| 152 | + """Extracts thought_signature bytes from a LiteLLM tool call id.""" |
| 153 | + if not tool_call_id: |
| 154 | + return tool_call_id, None |
| 155 | + |
| 156 | + tool_call_id, separator, encoded_signature = tool_call_id.partition( |
| 157 | + _LITELLM_THOUGHT_SIGNATURE_SEPARATOR |
| 158 | + ) |
| 159 | + if not separator or not encoded_signature: |
| 160 | + return tool_call_id, None |
| 161 | + |
| 162 | + try: |
| 163 | + return tool_call_id, base64.b64decode(encoded_signature) |
| 164 | + except (ValueError, TypeError) as err: |
| 165 | + logger.warning( |
| 166 | + "Failed to decode thought_signature from tool call id %r: %s", |
| 167 | + tool_call_id, |
| 168 | + err, |
| 169 | + ) |
| 170 | + return tool_call_id, None |
| 171 | + |
| 172 | + |
| 173 | +def _encode_litellm_tool_call_id( |
| 174 | + tool_call_id: Optional[str], thought_signature: Optional[bytes] |
| 175 | +) -> Optional[str]: |
| 176 | + """Embeds thought_signature bytes in a LiteLLM-compatible tool call id.""" |
| 177 | + if not tool_call_id or not thought_signature: |
| 178 | + return tool_call_id |
| 179 | + |
| 180 | + encoded_signature = base64.b64encode(thought_signature).decode("utf-8") |
| 181 | + return ( |
| 182 | + f"{tool_call_id}{_LITELLM_THOUGHT_SIGNATURE_SEPARATOR}" |
| 183 | + f"{encoded_signature}" |
| 184 | + ) |
| 185 | + |
146 | 186 | _LITELLM_IMPORTED = False |
147 | 187 | _LITELLM_GLOBAL_SYMBOLS = ( |
148 | 188 | "ChatCompletionAssistantMessage", |
@@ -665,7 +705,10 @@ async def _content_to_message_param( |
665 | 705 | tool_calls.append( |
666 | 706 | ChatCompletionAssistantToolCall( |
667 | 707 | type="function", |
668 | | - id=part.function_call.id, |
| 708 | + id=_encode_litellm_tool_call_id( |
| 709 | + part.function_call.id, |
| 710 | + part.thought_signature, |
| 711 | + ), |
669 | 712 | function=Function( |
670 | 713 | name=part.function_call.name, |
671 | 714 | arguments=_safe_json_serialize(part.function_call.args), |
@@ -1481,7 +1524,12 @@ def _message_to_generate_content_response( |
1481 | 1524 | name=tool_call.function.name, |
1482 | 1525 | args=json.loads(tool_call.function.arguments or "{}"), |
1483 | 1526 | ) |
1484 | | - part.function_call.id = tool_call.id |
| 1527 | + tool_call_id, thought_signature = _decode_litellm_tool_call_id( |
| 1528 | + tool_call.id |
| 1529 | + ) |
| 1530 | + part.function_call.id = tool_call_id |
| 1531 | + if thought_signature: |
| 1532 | + part.thought_signature = thought_signature |
1485 | 1533 | parts.append(part) |
1486 | 1534 |
|
1487 | 1535 | return LlmResponse( |
|
0 commit comments