mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-13 08:52:02 +00:00
fix(proxy): keep failing requests on keys with non-object logging metadata
A key whose stored metadata.logging is not a list of objects failed every request with a 500 before this branch. The credential reference rewrite turned those values into no entries, so the request quietly succeeded without the key's logging. Raise a TypeError for those shapes again so the request path behaves as it did on main, while empty values and objects with unknown fields still go through the existing skip.
This commit is contained in:
parent
0c3e369e3e
commit
7ac4ab1356
2 changed files with 40 additions and 5 deletions
|
|
@ -750,11 +750,14 @@ def key_logging_entries(metadata: object) -> tuple[Mapping[str, object], ...] |
|
||||||
entries: Final = decrypted_logging_entries(metadata)
|
entries: Final = decrypted_logging_entries(metadata)
|
||||||
if entries is None:
|
if entries is None:
|
||||||
return None
|
return None
|
||||||
return tuple(
|
return tuple(resolved for entry in _logging_objects(entries) if (resolved := _with_credential(entry)) is not None)
|
||||||
resolved
|
|
||||||
for entry in object_sequence(entries) or ()
|
|
||||||
if (resolved := _with_credential(string_keyed(entry))) is not None
|
def _logging_objects(entries: object) -> tuple[Mapping[str, object], ...]:
|
||||||
)
|
items: Final = () if entries in ("", {}) else object_sequence(entries)
|
||||||
|
if items is None or not all(isinstance(item, Mapping) for item in items):
|
||||||
|
raise TypeError(f"key metadata.logging must be a list of objects, got {type(entries).__name__}")
|
||||||
|
return tuple(string_keyed(item) for item in items)
|
||||||
|
|
||||||
|
|
||||||
def _with_credential(entry: Mapping[str, object]) -> Mapping[str, object] | None:
|
def _with_credential(entry: Mapping[str, object]) -> Mapping[str, object] | None:
|
||||||
|
|
|
||||||
|
|
@ -848,3 +848,35 @@ def test_key_logging_entries_drops_an_entry_whose_credential_is_not_defined(monk
|
||||||
]
|
]
|
||||||
|
|
||||||
assert key_logging_entries({"logging": entries}) == (kept,)
|
assert key_logging_entries({"logging": entries}) == (kept,)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"logging_value",
|
||||||
|
[
|
||||||
|
pytest.param("abc", id="string"),
|
||||||
|
pytest.param({"callback_name": "arize"}, id="object"),
|
||||||
|
pytest.param(5, id="number"),
|
||||||
|
pytest.param(["x"], id="list-with-string"),
|
||||||
|
pytest.param([{"callback_name": "langfuse", "callback_vars": {}}, None], id="list-with-null"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_key_logging_entries_fails_on_non_object_logging_like_the_request_path_always_has(
|
||||||
|
logging_value: object,
|
||||||
|
) -> None:
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
key_logging_entries({"logging": logging_value})
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("logging_value", "expected"),
|
||||||
|
[
|
||||||
|
pytest.param("", (), id="empty-string"),
|
||||||
|
pytest.param({}, (), id="empty-object"),
|
||||||
|
pytest.param([], (), id="empty-list"),
|
||||||
|
pytest.param([{"bad": 1}], ({"bad": 1},), id="object-with-unknown-fields"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_key_logging_entries_passes_empty_and_object_entries_through(
|
||||||
|
logging_value: object, expected: tuple[dict[str, object], ...]
|
||||||
|
) -> None:
|
||||||
|
assert key_logging_entries({"logging": logging_value}) == expected
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue