mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-14 09:03:16 +00:00
feat(proxy): add global raw response model opt-out
This commit is contained in:
parent
dc741a6e16
commit
9a627285bc
7 changed files with 60 additions and 5 deletions
|
|
@ -2480,6 +2480,14 @@ class ConfigGeneralSettings(LiteLLMPydanticObjectBase):
|
|||
Documents all the fields supported by `general_settings` in config.yaml
|
||||
"""
|
||||
|
||||
return_raw_model_name: bool = Field(
|
||||
False,
|
||||
description=(
|
||||
"Preserve the upstream response model instead of replacing it with the requested model alias. "
|
||||
"Applies to streaming and non-streaming responses. The per-request raw-model opt-out remains supported."
|
||||
),
|
||||
)
|
||||
|
||||
use_spend_logs_ledger: bool = Field(
|
||||
False,
|
||||
description="Apply request accounting from stored SpendLogs deliveries. Enable only after all accounting writers are upgraded and ledger activation gates pass.",
|
||||
|
|
|
|||
|
|
@ -357,8 +357,10 @@ def resolve_litellm_call_id(client_call_id: str | None) -> str:
|
|||
return str(uuid.uuid4())
|
||||
|
||||
|
||||
def should_return_raw_model_name(request_data: dict[str, object]) -> bool:
|
||||
return any(
|
||||
def should_return_raw_model_name(
|
||||
request_data: dict[str, object], general_settings: Mapping[str, object] | None = None
|
||||
) -> bool:
|
||||
return (general_settings is not None and general_settings.get("return_raw_model_name") is True) or any(
|
||||
isinstance(metadata, dict) and metadata.get(RETURN_RAW_MODEL_NAME_METADATA_KEY) is True
|
||||
for metadata in (request_data.get("metadata"), request_data.get("litellm_metadata"))
|
||||
)
|
||||
|
|
@ -3314,7 +3316,9 @@ class ProxyBaseLLMRequestProcessing:
|
|||
proxy_logging_obj=proxy_logging_obj,
|
||||
request=request,
|
||||
restamp_model=(
|
||||
None if should_return_raw_model_name(self.data) else requested_model_from_client
|
||||
None
|
||||
if should_return_raw_model_name(self.data, general_settings)
|
||||
else requested_model_from_client
|
||||
),
|
||||
)
|
||||
selected_data_generator = wrap_sse_stream_with_keepalive_pings(
|
||||
|
|
@ -3480,7 +3484,7 @@ class ProxyBaseLLMRequestProcessing:
|
|||
response_obj=response,
|
||||
requested_model=requested_model_from_client,
|
||||
log_context=f"litellm_call_id={logging_obj.litellm_call_id}",
|
||||
return_raw_model_name=should_return_raw_model_name(self.data),
|
||||
return_raw_model_name=should_return_raw_model_name(self.data, general_settings),
|
||||
)
|
||||
|
||||
fastapi_response.headers.update(
|
||||
|
|
|
|||
|
|
@ -199,6 +199,7 @@ model_list:
|
|||
|
||||
general_settings:
|
||||
master_key: os.environ/LITELLM_MASTER_KEY
|
||||
return_raw_model_name: false
|
||||
# Opt-in: let CheckBatchCost track cost for unmanaged batches created with a raw
|
||||
# gs:// (Vertex) or s3:// (Bedrock) input_file_id. Requires a matching deployment
|
||||
# configured for the batched model. Defaults to false.
|
||||
|
|
|
|||
|
|
@ -10268,8 +10268,9 @@ def _restamp_streaming_chunk_model(
|
|||
model_mismatch_logged: bool,
|
||||
fallback_was_attempted: bool = False,
|
||||
fallback_model_from_metadata: str | None = None,
|
||||
response_model_settings: Mapping[str, object] | None = None,
|
||||
) -> tuple[object, bool]:
|
||||
if should_return_raw_model_name(request_data):
|
||||
if should_return_raw_model_name(request_data, response_model_settings):
|
||||
return chunk, model_mismatch_logged
|
||||
|
||||
target_model: Final = fallback_model_from_metadata if fallback_was_attempted else requested_model_from_client
|
||||
|
|
@ -10918,6 +10919,9 @@ async def async_data_generator(
|
|||
model_mismatch_logged=model_mismatch_logged,
|
||||
fallback_was_attempted=fallback_was_attempted,
|
||||
fallback_model_from_metadata=fallback_model_from_metadata,
|
||||
response_model_settings=cast( # cast-ok: proxy configuration validates the legacy untyped settings global
|
||||
Mapping[str, object], general_settings
|
||||
),
|
||||
)
|
||||
|
||||
if strip_stream_usage and is_injected_stream_usage_artifact(chunk):
|
||||
|
|
@ -20592,6 +20596,7 @@ _GENERAL_SETTINGS_CONFIG_LIST_FIELD_TYPES: Final[Mapping[str, str]] = MappingPro
|
|||
"mcp_xff_num_trusted_hops": "Integer",
|
||||
"mcp_prefer_client_id_metadata_document": "Boolean",
|
||||
"always_include_stream_usage": "Boolean",
|
||||
"return_raw_model_name": "Boolean",
|
||||
"forward_client_headers_to_llm_api": "Boolean",
|
||||
"mcp_required_fields": "List",
|
||||
"mcp_disabled_capabilities": "List",
|
||||
|
|
|
|||
|
|
@ -320,6 +320,20 @@ def test_restamp_streaming_chunk_model_respects_raw_model_name_toggle(return_raw
|
|||
assert logged is (not return_raw_model_name)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("global_opt_out", [False, True])
|
||||
def test_restamp_streaming_chunk_model_respects_global_raw_model_name(global_opt_out: bool) -> None:
|
||||
chunk: Final = _simple_chunk(model="provider-model")
|
||||
new_chunk, logged = _restamp_streaming_chunk_model(
|
||||
chunk=chunk,
|
||||
requested_model_from_client="public-alias",
|
||||
request_data={},
|
||||
model_mismatch_logged=False,
|
||||
response_model_settings={"return_raw_model_name": global_opt_out},
|
||||
)
|
||||
assert new_chunk.model == ("provider-model" if global_opt_out else "public-alias")
|
||||
assert logged is (not global_opt_out)
|
||||
|
||||
|
||||
def test_restamp_streaming_chunk_model_overrides_model_on_dict():
|
||||
chunk = {"model": "internal", "choices": []}
|
||||
new_chunk, logged = _restamp_streaming_chunk_model(
|
||||
|
|
|
|||
|
|
@ -3277,6 +3277,23 @@ class TestOverrideOpenAIResponseModel:
|
|||
def test_raw_model_name_toggle_metadata(self, request_data, expected):
|
||||
assert should_return_raw_model_name(request_data) is expected
|
||||
|
||||
@pytest.mark.parametrize("global_opt_out", [False, True])
|
||||
@pytest.mark.parametrize("request_opt_out", [False, True])
|
||||
def test_global_raw_model_name_preserves_upstream_response(
|
||||
self, global_opt_out: bool, request_opt_out: bool
|
||||
) -> None:
|
||||
response_obj: Final = {"model": "provider-model"}
|
||||
_override_openai_response_model(
|
||||
response_obj=response_obj,
|
||||
requested_model="public-alias",
|
||||
log_context="test_context",
|
||||
return_raw_model_name=should_return_raw_model_name(
|
||||
{"litellm_metadata": {RETURN_RAW_MODEL_NAME_METADATA_KEY: request_opt_out}},
|
||||
{"return_raw_model_name": global_opt_out},
|
||||
),
|
||||
)
|
||||
assert response_obj["model"] == ("provider-model" if global_opt_out or request_opt_out else "public-alias")
|
||||
|
||||
def test_override_model_preserves_fallback_model_when_fallback_occurred_object(
|
||||
self,
|
||||
):
|
||||
|
|
|
|||
6
ui/litellm-dashboard/src/lib/http/schema.d.ts
generated
vendored
6
ui/litellm-dashboard/src/lib/http/schema.d.ts
generated
vendored
|
|
@ -30799,6 +30799,12 @@ export interface components {
|
|||
* @default 3600
|
||||
*/
|
||||
responses_websocket_session_limit_seconds: number;
|
||||
/**
|
||||
* Return Raw Model Name
|
||||
* @description Preserve the upstream response model instead of replacing it with the requested model alias. Applies to streaming and non-streaming responses. The per-request raw-model opt-out remains supported.
|
||||
* @default false
|
||||
*/
|
||||
return_raw_model_name: boolean;
|
||||
/**
|
||||
* Return Wildcard Routes
|
||||
* @description When true, `/v1/models` lists wildcard routes like `openai/*` unless the request passes `return_wildcard_routes=false`.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue