mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-15 04:19:08 +00:00
fix(tinyfish): apply --line-length 88 ruff format to match CI
CI uses 'ruff format --check --line-length 88'; my prior format pass used the default line length, leaving several lines unwrapped. No behavior change — purely whitespace.
This commit is contained in:
parent
b9923a7cb7
commit
656cdcb60d
3 changed files with 173 additions and 56 deletions
|
|
@ -65,7 +65,9 @@ class TinyfishSearchConfig(BaseSearchConfig):
|
|||
default_api_base=self.TINYFISH_API_BASE,
|
||||
)
|
||||
if not resolved_key:
|
||||
raise ValueError("TINYFISH_API_KEY is not set. Set `TINYFISH_API_KEY` environment variable.")
|
||||
raise ValueError(
|
||||
"TINYFISH_API_KEY is not set. Set `TINYFISH_API_KEY` environment variable."
|
||||
)
|
||||
return {**headers, "X-API-Key": resolved_key, "Accept": "application/json"}
|
||||
|
||||
def get_complete_url(
|
||||
|
|
@ -75,9 +77,13 @@ class TinyfishSearchConfig(BaseSearchConfig):
|
|||
data: dict[str, object] | list[dict[str, object]] | None = None,
|
||||
**kwargs: object,
|
||||
) -> str:
|
||||
resolved_base = api_base or get_secret_str("TINYFISH_API_BASE") or self.TINYFISH_API_BASE
|
||||
resolved_base = (
|
||||
api_base or get_secret_str("TINYFISH_API_BASE") or self.TINYFISH_API_BASE
|
||||
)
|
||||
if isinstance(data, dict) and _TINYFISH_PARAMS_KEY in data:
|
||||
validated_params = _UrlEncodableParams.validate_python(data[_TINYFISH_PARAMS_KEY])
|
||||
validated_params = _UrlEncodableParams.validate_python(
|
||||
data[_TINYFISH_PARAMS_KEY]
|
||||
)
|
||||
return f"{resolved_base}?{urlencode(validated_params, doseq=True)}"
|
||||
return resolved_base
|
||||
|
||||
|
|
@ -113,7 +119,9 @@ class TinyfishSearchConfig(BaseSearchConfig):
|
|||
resolved_query = " ".join(query) if isinstance(query, list) else query
|
||||
|
||||
try:
|
||||
domains = _StrList.validate_python(optional_params.get("search_domain_filter"))
|
||||
domains = _StrList.validate_python(
|
||||
optional_params.get("search_domain_filter")
|
||||
)
|
||||
except (ValidationError, TypeError):
|
||||
domains = []
|
||||
if domains:
|
||||
|
|
@ -131,7 +139,9 @@ class TinyfishSearchConfig(BaseSearchConfig):
|
|||
raw_max = optional_params.get("max_results")
|
||||
if isinstance(raw_max, (int, float, str)):
|
||||
try:
|
||||
self._caller_max_results = max(1, min(int(raw_max), _TINYFISH_RESULT_CAP))
|
||||
self._caller_max_results = max(
|
||||
1, min(int(raw_max), _TINYFISH_RESULT_CAP)
|
||||
)
|
||||
except (ValueError, TypeError, OverflowError):
|
||||
# OverflowError covers int(float('inf')) and similar non-finite floats.
|
||||
verbose_logger.warning(
|
||||
|
|
@ -202,7 +212,9 @@ class TinyfishSearchConfig(BaseSearchConfig):
|
|||
)
|
||||
|
||||
try:
|
||||
raw_json: object = raw_response.json() # any-ok: httpx Response.json() -> Any
|
||||
raw_json: object = (
|
||||
raw_response.json()
|
||||
) # any-ok: httpx Response.json() -> Any
|
||||
except json.JSONDecodeError:
|
||||
raise self._wrap_error(
|
||||
error_message=f"Expected JSON response, got: {raw_response.text[:200]}",
|
||||
|
|
@ -226,7 +238,9 @@ class TinyfishSearchConfig(BaseSearchConfig):
|
|||
parsed = SearchResponse.model_validate(raw_json)
|
||||
except ValidationError as e:
|
||||
raise self._wrap_error(
|
||||
error_message=(f"Response shape does not match LiteLLM's SearchResponse schema: {e}"),
|
||||
error_message=(
|
||||
f"Response shape does not match LiteLLM's SearchResponse schema: {e}"
|
||||
),
|
||||
status_code=raw_response.status_code,
|
||||
headers=dict(raw_response.headers),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -34,7 +34,9 @@ MOCK_TINYFISH_RESPONSE = {
|
|||
}
|
||||
|
||||
|
||||
def _make_mock_response(json_data: dict, status_code: int = 200, request_url: str | None = None) -> MagicMock:
|
||||
def _make_mock_response(
|
||||
json_data: dict, status_code: int = 200, request_url: str | None = None
|
||||
) -> MagicMock:
|
||||
mock = MagicMock()
|
||||
mock.status_code = status_code
|
||||
mock.json.return_value = json_data
|
||||
|
|
|
|||
|
|
@ -100,39 +100,53 @@ class TestTinyfishSearchConfig:
|
|||
class TestTransformSearchRequest:
|
||||
def test_basic_query(self):
|
||||
config = TinyfishSearchConfig()
|
||||
result = config.transform_search_request(query="hello world", optional_params={})
|
||||
result = config.transform_search_request(
|
||||
query="hello world", optional_params={}
|
||||
)
|
||||
assert result == {"_tinyfish_params": {"query": "hello world"}}
|
||||
|
||||
def test_list_query_joined(self):
|
||||
config = TinyfishSearchConfig()
|
||||
result = config.transform_search_request(query=["hello", "world"], optional_params={})
|
||||
result = config.transform_search_request(
|
||||
query=["hello", "world"], optional_params={}
|
||||
)
|
||||
assert result["_tinyfish_params"]["query"] == "hello world"
|
||||
|
||||
def test_country_maps_to_location(self):
|
||||
config = TinyfishSearchConfig()
|
||||
result = config.transform_search_request(query="test", optional_params={"country": "US"})
|
||||
result = config.transform_search_request(
|
||||
query="test", optional_params={"country": "US"}
|
||||
)
|
||||
assert result["_tinyfish_params"]["location"] == "US"
|
||||
|
||||
def test_max_results_not_sent_on_wire(self):
|
||||
# TinyFish doesn't honor max_results server-side; we apply it client-side
|
||||
# in transform_search_response. The querystring should be free of it.
|
||||
config = TinyfishSearchConfig()
|
||||
result = config.transform_search_request(query="test", optional_params={"max_results": 5})
|
||||
result = config.transform_search_request(
|
||||
query="test", optional_params={"max_results": 5}
|
||||
)
|
||||
assert "max_results" not in result["_tinyfish_params"]
|
||||
|
||||
def test_max_results_clamped_upper_stored_on_self(self):
|
||||
config = TinyfishSearchConfig()
|
||||
config.transform_search_request(query="test", optional_params={"max_results": 100})
|
||||
config.transform_search_request(
|
||||
query="test", optional_params={"max_results": 100}
|
||||
)
|
||||
assert config._caller_max_results == 10 # TinyFish's natural cap
|
||||
|
||||
def test_max_results_clamped_lower_stored_on_self(self):
|
||||
config = TinyfishSearchConfig()
|
||||
config.transform_search_request(query="test", optional_params={"max_results": 0})
|
||||
config.transform_search_request(
|
||||
query="test", optional_params={"max_results": 0}
|
||||
)
|
||||
assert config._caller_max_results == 1
|
||||
|
||||
def test_max_results_normal_stored_on_self(self):
|
||||
config = TinyfishSearchConfig()
|
||||
config.transform_search_request(query="test", optional_params={"max_results": 5})
|
||||
config.transform_search_request(
|
||||
query="test", optional_params={"max_results": 5}
|
||||
)
|
||||
assert config._caller_max_results == 5
|
||||
|
||||
def test_max_results_non_numeric_string_warns_and_skips(self, caplog):
|
||||
|
|
@ -140,7 +154,9 @@ class TestTransformSearchRequest:
|
|||
# via warning and treats the value as if max_results wasn't set.
|
||||
config = TinyfishSearchConfig()
|
||||
with caplog.at_level("WARNING"):
|
||||
result = config.transform_search_request(query="test", optional_params={"max_results": "abc"})
|
||||
result = config.transform_search_request(
|
||||
query="test", optional_params={"max_results": "abc"}
|
||||
)
|
||||
assert config._caller_max_results is None
|
||||
assert "max_results" not in result["_tinyfish_params"]
|
||||
messages = [r.getMessage() for r in caplog.records]
|
||||
|
|
@ -152,7 +168,9 @@ class TestTransformSearchRequest:
|
|||
# warn-and-ignore behavior as other malformed values.
|
||||
config = TinyfishSearchConfig()
|
||||
with caplog.at_level("WARNING"):
|
||||
result = config.transform_search_request(query="test", optional_params={"max_results": float("inf")})
|
||||
result = config.transform_search_request(
|
||||
query="test", optional_params={"max_results": float("inf")}
|
||||
)
|
||||
assert config._caller_max_results is None
|
||||
assert "max_results" not in result["_tinyfish_params"]
|
||||
messages = [r.getMessage() for r in caplog.records]
|
||||
|
|
@ -171,17 +189,23 @@ class TestTransformSearchRequest:
|
|||
|
||||
def test_domain_filter_empty_list_ignored(self):
|
||||
config = TinyfishSearchConfig()
|
||||
result = config.transform_search_request(query="test", optional_params={"search_domain_filter": []})
|
||||
result = config.transform_search_request(
|
||||
query="test", optional_params={"search_domain_filter": []}
|
||||
)
|
||||
assert result["_tinyfish_params"]["query"] == "test"
|
||||
|
||||
def test_domain_filter_non_list_ignored(self):
|
||||
config = TinyfishSearchConfig()
|
||||
result = config.transform_search_request(query="test", optional_params={"search_domain_filter": "not-a-list"})
|
||||
result = config.transform_search_request(
|
||||
query="test", optional_params={"search_domain_filter": "not-a-list"}
|
||||
)
|
||||
assert result["_tinyfish_params"]["query"] == "test"
|
||||
|
||||
def test_unknown_params_passed_through(self):
|
||||
config = TinyfishSearchConfig()
|
||||
result = config.transform_search_request(query="test", optional_params={"language": "en", "page": 2})
|
||||
result = config.transform_search_request(
|
||||
query="test", optional_params={"language": "en", "page": 2}
|
||||
)
|
||||
params = result["_tinyfish_params"]
|
||||
assert params["language"] == "en"
|
||||
assert params["page"] == 2
|
||||
|
|
@ -191,7 +215,9 @@ class TestTransformSearchRequest:
|
|||
supported = config.get_supported_perplexity_optional_params()
|
||||
if supported:
|
||||
param = next(p for p in supported if p != "max_results" and p != "country")
|
||||
result = config.transform_search_request(query="test", optional_params={param: "value"})
|
||||
result = config.transform_search_request(
|
||||
query="test", optional_params={param: "value"}
|
||||
)
|
||||
assert param not in result["_tinyfish_params"]
|
||||
|
||||
def test_arbitrary_param_passed_through(self):
|
||||
|
|
@ -199,7 +225,9 @@ class TestTransformSearchRequest:
|
|||
# The passthrough loop should forward it verbatim without LiteLLM needing
|
||||
# to know about it.
|
||||
config = TinyfishSearchConfig()
|
||||
result = config.transform_search_request(query="test", optional_params={"fetch": "{}"})
|
||||
result = config.transform_search_request(
|
||||
query="test", optional_params={"fetch": "{}"}
|
||||
)
|
||||
assert result["_tinyfish_params"]["fetch"] == "{}"
|
||||
|
||||
def test_dict_param_auto_json_encoded(self):
|
||||
|
|
@ -210,15 +238,22 @@ class TestTransformSearchRequest:
|
|||
query="test",
|
||||
optional_params={"fetch": {"format": "html", "fetch_path": "fast"}},
|
||||
)
|
||||
assert result["_tinyfish_params"]["fetch"] == '{"format":"html","fetch_path":"fast"}'
|
||||
assert (
|
||||
result["_tinyfish_params"]["fetch"]
|
||||
== '{"format":"html","fetch_path":"fast"}'
|
||||
)
|
||||
|
||||
def test_bool_param_serialized_as_lowercase(self):
|
||||
# urlencode renders Python bool as capitalized "True"/"False"; ux-labs
|
||||
# rejects those (e.g. include_thumbnail must be literal "true"/"false").
|
||||
# Normalize before passing through.
|
||||
config = TinyfishSearchConfig()
|
||||
true_result = config.transform_search_request(query="test", optional_params={"include_thumbnail": True})
|
||||
false_result = config.transform_search_request(query="test", optional_params={"include_thumbnail": False})
|
||||
true_result = config.transform_search_request(
|
||||
query="test", optional_params={"include_thumbnail": True}
|
||||
)
|
||||
false_result = config.transform_search_request(
|
||||
query="test", optional_params={"include_thumbnail": False}
|
||||
)
|
||||
assert true_result["_tinyfish_params"]["include_thumbnail"] == "true"
|
||||
assert false_result["_tinyfish_params"]["include_thumbnail"] == "false"
|
||||
|
||||
|
|
@ -226,7 +261,9 @@ class TestTransformSearchRequest:
|
|||
# If the caller already JSON-encoded, don't re-encode.
|
||||
config = TinyfishSearchConfig()
|
||||
already = '{"format":"html"}'
|
||||
result = config.transform_search_request(query="test", optional_params={"fetch": already})
|
||||
result = config.transform_search_request(
|
||||
query="test", optional_params={"fetch": already}
|
||||
)
|
||||
assert result["_tinyfish_params"]["fetch"] == already
|
||||
|
||||
|
||||
|
|
@ -242,7 +279,9 @@ class TestGetCompleteUrl:
|
|||
|
||||
def test_custom_api_base(self):
|
||||
config = TinyfishSearchConfig()
|
||||
url = config.get_complete_url(api_base="https://custom.api.tinyfish.ai", optional_params={})
|
||||
url = config.get_complete_url(
|
||||
api_base="https://custom.api.tinyfish.ai", optional_params={}
|
||||
)
|
||||
assert url == "https://custom.api.tinyfish.ai"
|
||||
|
||||
def test_env_api_base(self):
|
||||
|
|
@ -275,7 +314,9 @@ class TestGetCompleteUrl:
|
|||
"litellm.llms.tinyfish.search.transformation.get_secret_str",
|
||||
return_value=None,
|
||||
):
|
||||
url = config.get_complete_url(api_base=None, optional_params={}, data={"other": "value"})
|
||||
url = config.get_complete_url(
|
||||
api_base=None, optional_params={}, data={"other": "value"}
|
||||
)
|
||||
assert url == "https://api.search.tinyfish.ai"
|
||||
|
||||
def test_data_none(self):
|
||||
|
|
@ -292,17 +333,23 @@ class TestTransformSearchResponse:
|
|||
def test_basic_response(self):
|
||||
config = TinyfishSearchConfig()
|
||||
mock_response = _make_mock_response(MOCK_TINYFISH_RESPONSE)
|
||||
result = config.transform_search_response(raw_response=mock_response, logging_obj=None)
|
||||
result = config.transform_search_response(
|
||||
raw_response=mock_response, logging_obj=None
|
||||
)
|
||||
assert result.object == "search"
|
||||
assert len(result.results) == 2
|
||||
assert result.results[0].title == "TinyFish - AI Web Automation"
|
||||
assert result.results[0].url == "https://tinyfish.ai"
|
||||
assert result.results[0].snippet == "Automate any website with natural language."
|
||||
assert (
|
||||
result.results[0].snippet == "Automate any website with natural language."
|
||||
)
|
||||
|
||||
def test_empty_results(self):
|
||||
config = TinyfishSearchConfig()
|
||||
mock_response = _make_mock_response({"results": []})
|
||||
result = config.transform_search_response(raw_response=mock_response, logging_obj=None)
|
||||
result = config.transform_search_response(
|
||||
raw_response=mock_response, logging_obj=None
|
||||
)
|
||||
assert result.object == "search"
|
||||
assert len(result.results) == 0
|
||||
|
||||
|
|
@ -321,7 +368,9 @@ class TestTransformSearchResponse:
|
|||
]
|
||||
}
|
||||
mock_response = _make_mock_response(many_results)
|
||||
result = config.transform_search_response(raw_response=mock_response, logging_obj=None)
|
||||
result = config.transform_search_response(
|
||||
raw_response=mock_response, logging_obj=None
|
||||
)
|
||||
assert len(result.results) == 3
|
||||
assert result.results[0].title == "Result 0"
|
||||
assert result.results[2].title == "Result 2"
|
||||
|
|
@ -340,7 +389,9 @@ class TestTransformSearchResponse:
|
|||
]
|
||||
}
|
||||
mock_response = _make_mock_response(many_results)
|
||||
result = config.transform_search_response(raw_response=mock_response, logging_obj=None)
|
||||
result = config.transform_search_response(
|
||||
raw_response=mock_response, logging_obj=None
|
||||
)
|
||||
assert len(result.results) == 10
|
||||
|
||||
def test_missing_required_fields_default_to_empty_string(self):
|
||||
|
|
@ -348,8 +399,12 @@ class TestTransformSearchResponse:
|
|||
# We default missing/null values to "" so a degraded TinyFish result
|
||||
# flows through instead of failing the whole call.
|
||||
config = TinyfishSearchConfig()
|
||||
mock_response = _make_mock_response({"results": [{}, {"title": None, "url": None, "snippet": None}]})
|
||||
result = config.transform_search_response(raw_response=mock_response, logging_obj=None)
|
||||
mock_response = _make_mock_response(
|
||||
{"results": [{}, {"title": None, "url": None, "snippet": None}]}
|
||||
)
|
||||
result = config.transform_search_response(
|
||||
raw_response=mock_response, logging_obj=None
|
||||
)
|
||||
assert len(result.results) == 2
|
||||
for r in result.results:
|
||||
assert r.title == ""
|
||||
|
|
@ -359,7 +414,9 @@ class TestTransformSearchResponse:
|
|||
def test_extra_per_result_fields_surface_as_attributes(self):
|
||||
config = TinyfishSearchConfig()
|
||||
mock_response = _make_mock_response(MOCK_TINYFISH_RESPONSE)
|
||||
result = config.transform_search_response(raw_response=mock_response, logging_obj=None)
|
||||
result = config.transform_search_response(
|
||||
raw_response=mock_response, logging_obj=None
|
||||
)
|
||||
first = result.results[0]
|
||||
assert getattr(first, "position", None) == 1
|
||||
assert getattr(first, "site_name", None) == "tinyfish.ai"
|
||||
|
|
@ -385,7 +442,9 @@ class TestTransformSearchResponse:
|
|||
]
|
||||
}
|
||||
mock_response = _make_mock_response(fetched)
|
||||
result = config.transform_search_response(raw_response=mock_response, logging_obj=None)
|
||||
result = config.transform_search_response(
|
||||
raw_response=mock_response, logging_obj=None
|
||||
)
|
||||
first = result.results[0]
|
||||
fetch_field = getattr(first, "fetch", None)
|
||||
assert isinstance(fetch_field, dict)
|
||||
|
|
@ -394,7 +453,9 @@ class TestTransformSearchResponse:
|
|||
def test_no_request_uses_default_max_results(self):
|
||||
config = TinyfishSearchConfig()
|
||||
mock_response = _make_mock_response(MOCK_TINYFISH_RESPONSE)
|
||||
result = config.transform_search_response(raw_response=mock_response, logging_obj=None)
|
||||
result = config.transform_search_response(
|
||||
raw_response=mock_response, logging_obj=None
|
||||
)
|
||||
assert len(result.results) == 2
|
||||
|
||||
def test_parameter_warnings_reader_emits_log_lines(self, caplog):
|
||||
|
|
@ -417,7 +478,9 @@ class TestTransformSearchResponse:
|
|||
}
|
||||
mock_response = _make_mock_response(body)
|
||||
with caplog.at_level("WARNING"):
|
||||
config.transform_search_response(raw_response=mock_response, logging_obj=None)
|
||||
config.transform_search_response(
|
||||
raw_response=mock_response, logging_obj=None
|
||||
)
|
||||
messages = [r.getMessage() for r in caplog.records]
|
||||
assert any("max_tokens_per_page" in m for m in messages)
|
||||
# The type is included in the message so agents can branch on it.
|
||||
|
|
@ -428,8 +491,12 @@ class TestTransformSearchResponse:
|
|||
config = TinyfishSearchConfig()
|
||||
mock_response = _make_mock_response(MOCK_TINYFISH_RESPONSE)
|
||||
with caplog.at_level("WARNING"):
|
||||
config.transform_search_response(raw_response=mock_response, logging_obj=None)
|
||||
assert not any("TinyFish Search ignored" in r.getMessage() for r in caplog.records)
|
||||
config.transform_search_response(
|
||||
raw_response=mock_response, logging_obj=None
|
||||
)
|
||||
assert not any(
|
||||
"TinyFish Search ignored" in r.getMessage() for r in caplog.records
|
||||
)
|
||||
|
||||
def test_parameter_warnings_malformed_shapes_never_throw(self):
|
||||
# Every shape that doesn't match {parameter: str, message: str} should
|
||||
|
|
@ -446,7 +513,9 @@ class TestTransformSearchResponse:
|
|||
]
|
||||
for bad_value in malformed_field_values:
|
||||
body = {"results": good_results, "parameter_warnings": bad_value}
|
||||
config.transform_search_response(raw_response=_make_mock_response(body), logging_obj=None) # must not raise
|
||||
config.transform_search_response(
|
||||
raw_response=_make_mock_response(body), logging_obj=None
|
||||
) # must not raise
|
||||
|
||||
malformed_entries = [
|
||||
"string in list", # non-dict
|
||||
|
|
@ -455,13 +524,23 @@ class TestTransformSearchResponse:
|
|||
{"type": "unsupported", "parameter": "x"}, # missing message
|
||||
{"type": "unsupported", "message": "y"}, # missing parameter
|
||||
{"parameter": "x", "message": "y"}, # missing type
|
||||
{"type": "unsupported", "parameter": None, "message": "y"}, # null parameter
|
||||
{
|
||||
"type": "unsupported",
|
||||
"parameter": None,
|
||||
"message": "y",
|
||||
}, # null parameter
|
||||
{"type": "unsupported", "parameter": "x", "message": ""}, # empty message
|
||||
{"type": "unsupported", "parameter": 42, "message": "y"}, # non-string parameter
|
||||
{
|
||||
"type": "unsupported",
|
||||
"parameter": 42,
|
||||
"message": "y",
|
||||
}, # non-string parameter
|
||||
{"type": 1, "parameter": "x", "message": "y"}, # non-string type
|
||||
]
|
||||
body = {"results": good_results, "parameter_warnings": malformed_entries}
|
||||
config.transform_search_response(raw_response=_make_mock_response(body), logging_obj=None) # must not raise
|
||||
config.transform_search_response(
|
||||
raw_response=_make_mock_response(body), logging_obj=None
|
||||
) # must not raise
|
||||
|
||||
def test_parameter_warnings_malformed_entries_emit_nothing(self, caplog):
|
||||
config = TinyfishSearchConfig()
|
||||
|
|
@ -478,7 +557,9 @@ class TestTransformSearchResponse:
|
|||
],
|
||||
}
|
||||
with caplog.at_level("WARNING"):
|
||||
config.transform_search_response(raw_response=_make_mock_response(body), logging_obj=None)
|
||||
config.transform_search_response(
|
||||
raw_response=_make_mock_response(body), logging_obj=None
|
||||
)
|
||||
messages = [r.getMessage() for r in caplog.records]
|
||||
assert sum("parameter_warning" in m for m in messages) == 1
|
||||
assert any("valid_one" in m for m in messages)
|
||||
|
|
@ -497,7 +578,9 @@ class TestErrorHandling:
|
|||
}
|
||||
mock_response = _make_mock_response(body, status_code=400)
|
||||
with pytest.raises(Exception) as exc_info:
|
||||
config.transform_search_response(raw_response=mock_response, logging_obj=None)
|
||||
config.transform_search_response(
|
||||
raw_response=mock_response, logging_obj=None
|
||||
)
|
||||
msg = str(exc_info.value)
|
||||
assert "TinyFish Search:" in msg
|
||||
assert "query is required" in msg
|
||||
|
|
@ -507,9 +590,13 @@ class TestErrorHandling:
|
|||
def test_429_preserves_status_code_and_headers(self):
|
||||
config = TinyfishSearchConfig()
|
||||
body = {"error": {"code": "RATE_LIMIT_EXCEEDED", "message": "60 rpm"}}
|
||||
mock_response = _make_mock_response(body, status_code=429, headers={"Retry-After": "60"})
|
||||
mock_response = _make_mock_response(
|
||||
body, status_code=429, headers={"Retry-After": "60"}
|
||||
)
|
||||
with pytest.raises(Exception) as exc_info:
|
||||
config.transform_search_response(raw_response=mock_response, logging_obj=None)
|
||||
config.transform_search_response(
|
||||
raw_response=mock_response, logging_obj=None
|
||||
)
|
||||
assert getattr(exc_info.value, "status_code", None) == 429
|
||||
headers = getattr(exc_info.value, "headers", {}) or {}
|
||||
assert headers.get("Retry-After") == "60"
|
||||
|
|
@ -520,7 +607,9 @@ class TestErrorHandling:
|
|||
body = {"errors": [{"code": "10000", "message": "Internal"}]}
|
||||
mock_response = _make_mock_response(body, status_code=502)
|
||||
with pytest.raises(Exception) as exc_info:
|
||||
config.transform_search_response(raw_response=mock_response, logging_obj=None)
|
||||
config.transform_search_response(
|
||||
raw_response=mock_response, logging_obj=None
|
||||
)
|
||||
msg = str(exc_info.value)
|
||||
assert "TinyFish Search:" in msg
|
||||
# The raw JSON body string should appear in the message verbatim.
|
||||
|
|
@ -528,9 +617,13 @@ class TestErrorHandling:
|
|||
|
||||
def test_non_json_4xx_body_uses_raw_text(self):
|
||||
config = TinyfishSearchConfig()
|
||||
mock_response = _make_mock_response(json_data=None, status_code=502, text="<html>Bad Gateway</html>")
|
||||
mock_response = _make_mock_response(
|
||||
json_data=None, status_code=502, text="<html>Bad Gateway</html>"
|
||||
)
|
||||
with pytest.raises(Exception) as exc_info:
|
||||
config.transform_search_response(raw_response=mock_response, logging_obj=None)
|
||||
config.transform_search_response(
|
||||
raw_response=mock_response, logging_obj=None
|
||||
)
|
||||
msg = str(exc_info.value)
|
||||
assert "TinyFish Search:" in msg
|
||||
assert "Bad Gateway" in msg
|
||||
|
|
@ -538,9 +631,13 @@ class TestErrorHandling:
|
|||
def test_non_json_200_body_routes_through_get_error_class(self):
|
||||
# 200 but the body isn't JSON (degraded backend, CDN-injected page, etc.)
|
||||
config = TinyfishSearchConfig()
|
||||
mock_response = _make_mock_response(json_data=None, status_code=200, text="not json")
|
||||
mock_response = _make_mock_response(
|
||||
json_data=None, status_code=200, text="not json"
|
||||
)
|
||||
with pytest.raises(Exception) as exc_info:
|
||||
config.transform_search_response(raw_response=mock_response, logging_obj=None)
|
||||
config.transform_search_response(
|
||||
raw_response=mock_response, logging_obj=None
|
||||
)
|
||||
msg = str(exc_info.value)
|
||||
assert "TinyFish Search:" in msg
|
||||
assert "Expected JSON response" in msg
|
||||
|
|
@ -551,7 +648,9 @@ class TestErrorHandling:
|
|||
# they hit BaseSearchConfig.get_error_class via LiteLLM core.
|
||||
config = TinyfishSearchConfig()
|
||||
body = '{"error": {"code": "UNAUTHORIZED", "message": "bad key"}}'
|
||||
exc = config._wrap_error(error_message=body, status_code=401, headers={"x": "y"})
|
||||
exc = config._wrap_error(
|
||||
error_message=body, status_code=401, headers={"x": "y"}
|
||||
)
|
||||
msg = str(exc)
|
||||
assert "TinyFish Search:" in msg
|
||||
assert "bad key" in msg
|
||||
|
|
@ -565,7 +664,9 @@ class TestErrorHandling:
|
|||
config = TinyfishSearchConfig()
|
||||
mock_response = _make_mock_response({"query": "x"}) # no `results` key
|
||||
with pytest.raises(Exception) as exc_info:
|
||||
config.transform_search_response(raw_response=mock_response, logging_obj=None)
|
||||
config.transform_search_response(
|
||||
raw_response=mock_response, logging_obj=None
|
||||
)
|
||||
msg = str(exc_info.value)
|
||||
assert "TinyFish Search:" in msg
|
||||
assert "schema" in msg.lower()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue