mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-13 03:59:12 +00:00
fix: decode responses container tool objects
This commit is contained in:
parent
43ba0857b5
commit
f2867e180c
5 changed files with 114 additions and 37 deletions
|
|
@ -196,7 +196,7 @@ def _collect_response_file_search_vector_store_ids(data: Dict[str, Any]) -> set[
|
|||
return vector_store_ids
|
||||
|
||||
|
||||
def _collect_response_code_interpreter_container_ids(data: Dict[str, Any]) -> set[str]:
|
||||
def _collect_response_code_interpreter_container_ids(data: dict[str, Any]) -> set[str]:
|
||||
container_ids: set[str] = set()
|
||||
tools = data.get("tools")
|
||||
if not isinstance(tools, list):
|
||||
|
|
@ -232,7 +232,7 @@ async def _authorize_response_file_search_vector_stores(
|
|||
|
||||
|
||||
async def _authorize_response_code_interpreter_containers(
|
||||
data: Dict[str, Any],
|
||||
data: dict[str, Any],
|
||||
user_api_key_dict: UserAPIKeyAuth,
|
||||
) -> None:
|
||||
container_ids = _collect_response_code_interpreter_container_ids(data)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import base64
|
||||
import copy
|
||||
import re
|
||||
from typing import (
|
||||
Any,
|
||||
|
|
@ -585,6 +586,55 @@ class ResponsesAPIRequestUtils:
|
|||
decoded = ResponsesAPIRequestUtils._decode_container_id(container_id)
|
||||
return decoded.get("response_id", container_id)
|
||||
|
||||
@staticmethod
|
||||
def _get_tool_value(tool: Any, key: str) -> Any:
|
||||
if isinstance(tool, dict):
|
||||
return tool.get(key)
|
||||
return getattr(tool, key, None)
|
||||
|
||||
@staticmethod
|
||||
def _set_tool_value(tool: Any, key: str, value: Any) -> None:
|
||||
if isinstance(tool, dict):
|
||||
tool[key] = value
|
||||
return
|
||||
setattr(tool, key, value)
|
||||
|
||||
@staticmethod
|
||||
def get_decoded_container_payload_from_tool(
|
||||
tool: Any, require_code_interpreter: bool = False
|
||||
) -> Optional[DecodedResponseId]:
|
||||
if require_code_interpreter and ResponsesAPIRequestUtils._get_tool_value(tool, "type") != "code_interpreter":
|
||||
return None
|
||||
|
||||
container_id = ResponsesAPIRequestUtils._get_tool_value(tool, "container")
|
||||
if not isinstance(container_id, str):
|
||||
return None
|
||||
|
||||
decoded = ResponsesAPIRequestUtils._decode_container_id(container_id)
|
||||
original_id = decoded.get("response_id", container_id)
|
||||
if original_id == container_id:
|
||||
return None
|
||||
return decoded
|
||||
|
||||
@staticmethod
|
||||
def get_decoded_container_payload_from_tools(
|
||||
tools: Any,
|
||||
) -> Optional[DecodedResponseId]:
|
||||
if not isinstance(tools, list):
|
||||
return None
|
||||
for tool in tools:
|
||||
decoded = ResponsesAPIRequestUtils.get_decoded_container_payload_from_tool(
|
||||
tool, require_code_interpreter=True
|
||||
)
|
||||
if decoded is not None:
|
||||
return decoded
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def set_custom_llm_provider_if_missing(kwargs: dict[str, Any], custom_llm_provider: Optional[str]) -> None:
|
||||
if custom_llm_provider and "custom_llm_provider" not in kwargs:
|
||||
kwargs["custom_llm_provider"] = custom_llm_provider
|
||||
|
||||
@staticmethod
|
||||
def decode_container_ids_in_tools_for_request(
|
||||
tools: Optional[Iterable[Any]],
|
||||
|
|
@ -597,21 +647,16 @@ class ResponsesAPIRequestUtils:
|
|||
if tools is None:
|
||||
return tools
|
||||
|
||||
updated_tools: List[Any] = []
|
||||
updated_tools: list[Any] = []
|
||||
changed = False
|
||||
|
||||
for tool in tools:
|
||||
updated_tool = tool.copy() if isinstance(tool, dict) else tool
|
||||
if isinstance(updated_tool, dict):
|
||||
container_id = updated_tool.get("container")
|
||||
if isinstance(container_id, str):
|
||||
decoded = ResponsesAPIRequestUtils._decode_container_id(
|
||||
container_id
|
||||
)
|
||||
original_id = decoded.get("response_id", container_id)
|
||||
if original_id != container_id:
|
||||
updated_tool["container"] = original_id
|
||||
changed = True
|
||||
updated_tool = tool
|
||||
decoded = ResponsesAPIRequestUtils.get_decoded_container_payload_from_tool(tool)
|
||||
if decoded is not None:
|
||||
updated_tool = tool.copy() if isinstance(tool, dict) else copy.copy(tool)
|
||||
ResponsesAPIRequestUtils._set_tool_value(updated_tool, "container", decoded["response_id"])
|
||||
changed = True
|
||||
updated_tools.append(updated_tool)
|
||||
|
||||
if not changed:
|
||||
|
|
|
|||
|
|
@ -4479,27 +4479,15 @@ class Router:
|
|||
return response
|
||||
|
||||
@staticmethod
|
||||
def _decode_responses_api_tool_container_ids(kwargs: Dict[str, Any]) -> None:
|
||||
def _decode_responses_api_tool_container_ids(
|
||||
kwargs: dict[str, Any], custom_llm_provider: Optional[str] = None
|
||||
) -> None:
|
||||
from litellm.responses.utils import ResponsesAPIRequestUtils
|
||||
|
||||
decoded_container_payload = None
|
||||
ResponsesAPIRequestUtils.set_custom_llm_provider_if_missing(kwargs, custom_llm_provider)
|
||||
request_tools = kwargs.get("tools")
|
||||
if isinstance(request_tools, list):
|
||||
for tool in request_tools:
|
||||
if not isinstance(tool, dict) or tool.get("type") != "code_interpreter":
|
||||
continue
|
||||
container_id = tool.get("container")
|
||||
if not isinstance(container_id, str):
|
||||
continue
|
||||
decoded = ResponsesAPIRequestUtils._decode_container_id(container_id)
|
||||
original_id = decoded.get("response_id", container_id)
|
||||
if original_id != container_id:
|
||||
decoded_container_payload = decoded
|
||||
break
|
||||
|
||||
tools = ResponsesAPIRequestUtils.decode_container_ids_in_tools_for_request(
|
||||
request_tools
|
||||
)
|
||||
decoded_container_payload = ResponsesAPIRequestUtils.get_decoded_container_payload_from_tools(request_tools)
|
||||
tools = ResponsesAPIRequestUtils.decode_container_ids_in_tools_for_request(request_tools)
|
||||
if tools is not request_tools:
|
||||
kwargs["tools"] = tools
|
||||
if decoded_container_payload is None:
|
||||
|
|
@ -5551,9 +5539,7 @@ class Router:
|
|||
**kwargs,
|
||||
):
|
||||
if call_type == "responses":
|
||||
if custom_llm_provider and "custom_llm_provider" not in kwargs:
|
||||
kwargs["custom_llm_provider"] = custom_llm_provider
|
||||
self._decode_responses_api_tool_container_ids(kwargs)
|
||||
self._decode_responses_api_tool_container_ids(kwargs, custom_llm_provider)
|
||||
return self._generic_api_call_with_fallbacks(original_function=original_function, **kwargs)
|
||||
|
||||
return sync_wrapper
|
||||
|
|
@ -5657,8 +5643,7 @@ class Router:
|
|||
**kwargs,
|
||||
)
|
||||
elif call_type == "aresponses":
|
||||
if custom_llm_provider and "custom_llm_provider" not in kwargs:
|
||||
kwargs["custom_llm_provider"] = custom_llm_provider
|
||||
self._decode_responses_api_tool_container_ids(kwargs, custom_llm_provider)
|
||||
return await self._aresponses_with_streaming_fallbacks(
|
||||
original_function=original_function,
|
||||
**kwargs,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import sys
|
|||
import os
|
||||
import json
|
||||
import traceback
|
||||
from types import SimpleNamespace
|
||||
from typing import Optional
|
||||
from dotenv import load_dotenv
|
||||
from fastapi import Request
|
||||
|
|
@ -833,6 +834,30 @@ def test_router_responses_routes_managed_tool_container_id_to_owner_deployment()
|
|||
assert tools[0]["container"] == encoded_container_id
|
||||
|
||||
|
||||
def test_router_responses_routes_managed_tool_object_container_id_to_owner_deployment():
|
||||
from litellm.responses.utils import ResponsesAPIRequestUtils
|
||||
|
||||
router = Router(model_list=[])
|
||||
encoded_container_id = ResponsesAPIRequestUtils._build_container_id(
|
||||
custom_llm_provider="azure",
|
||||
model_id="azure-deployment-id",
|
||||
container_id="cntr_native_123",
|
||||
)
|
||||
tool = SimpleNamespace(type="code_interpreter", container=encoded_container_id)
|
||||
kwargs = {
|
||||
"model": "logical-model",
|
||||
"custom_llm_provider": "openai",
|
||||
"tools": [tool],
|
||||
}
|
||||
|
||||
router._decode_responses_api_tool_container_ids(kwargs)
|
||||
|
||||
assert kwargs["model"] == "azure-deployment-id"
|
||||
assert kwargs["custom_llm_provider"] == "azure"
|
||||
assert kwargs["tools"][0].container == "cntr_native_123"
|
||||
assert tool.container == encoded_container_id
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_init_vector_store_api_endpoints():
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import base64
|
|||
import json
|
||||
import os
|
||||
import sys
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
|
@ -164,6 +165,27 @@ class TestResponsesAPIRequestUtils:
|
|||
)
|
||||
assert plain_tuple_result == list(plain_tuple)
|
||||
|
||||
def test_decode_container_ids_in_tool_objects_for_request(self):
|
||||
encoded_container_id = ResponsesAPIRequestUtils._build_container_id(
|
||||
custom_llm_provider="azure",
|
||||
model_id="azure-deployment-id",
|
||||
container_id="cntr_native_123",
|
||||
)
|
||||
tool = SimpleNamespace(
|
||||
type="code_interpreter", container=encoded_container_id
|
||||
)
|
||||
tools = [tool]
|
||||
|
||||
updated_tools = (
|
||||
ResponsesAPIRequestUtils.decode_container_ids_in_tools_for_request(tools)
|
||||
)
|
||||
|
||||
assert updated_tools is not tools
|
||||
assert isinstance(updated_tools, list)
|
||||
assert updated_tools[0] is not tool
|
||||
assert updated_tools[0].container == "cntr_native_123"
|
||||
assert tool.container == encoded_container_id
|
||||
|
||||
def test_update_responses_api_response_id_with_model_id_handles_dict(self):
|
||||
"""Ensure _update_responses_api_response_id_with_model_id works with dict input"""
|
||||
responses_api_response = {"id": "resp_abc123"}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue