fix(oxylabs): report scrape failures instead of raising IndexError (#7044)

* fix(oxylabs): report scrape failures instead of raising IndexError

The oxylabs SDK logs HTTP errors and returns an empty response rather than
raising, so the unchecked `response.results[0]` in every Oxylabs tool turned a
rejected request into `IndexError: list index out of range`. Invalid credentials
-- the most likely first-run mistake -- gave no indication of the cause. A
result carrying a non-2xx `status_code` had the same problem one level down: the
job ran, the page did not come back, and the tool returned its empty content as
though the scrape had succeeded, handing the agent "[]".

Both are now reported as a `ToolFailure` naming what went wrong, so the agent
gets something it can act on and the framework records the call as failed:

    401 Unauthorized
    400 Bad Request - Parameter `parsing_instructions` can be used just with
    `parse` parameter set to `true`.

Because the SDK keeps the cause only in its own log, the failing call is run
with a handler attached to the `oxylabs` logger and the status, the API's
explanation and timeouts are read back off it. `code` and `retryable` are set
from the status, so 429 and 5xx are marked worth retrying. Nothing about the
caller's logging configuration is changed; an application that has silenced the
SDK still gets the generic failure.

Content that is neither a string nor a dict is also serialized properly:
`parsing_instructions` commonly yields a list, and the previous `str()`
fallback produced a Python repr with single quotes instead of JSON.

The client construction and response handling these four tools duplicated
verbatim now live in a shared `OxylabsBaseTool`, following the existing
`SerpApiBaseTool` pattern, so the handling above exists in one place. The
generated tool specs change only by the new `locale` field, confirming the
tools' public surface is otherwise untouched.

Also add the `locale` option to the Google Search config, which the docs
already documented but the config model silently dropped, and correct two
copy-paste errors in the docs across all four locales.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(oxylabs): keep concurrent scrape diagnoses apart

The error capture attached a fresh handler to the shared `oxylabs` logger for
each scrape, so two scrapes in flight at once each saw both errors. `_diagnose`
reads the first HTTP status it finds, so a timeout could be reported as the
other request's 400 -- `retryable=False` on a failure that was worth retrying.

One handler now serves every scrape and routes each record to the capture of
the call that caused it via a `ContextVar`, which isolates threads and asyncio
tasks alike. Serializing the captures would have fixed the cross-talk too, but
at the cost of running every scrape one at a time. The handler stays attached
once installed: it is inert outside a capture, and detaching it would race with
concurrent scrapes.

The regression test forces the interleaving -- one capture is held open while
the other call logs -- and fails against the previous implementation.

Also drive `config` through the public constructor in the tests instead of
assigning `__dict__["config"]`, so they would catch `__init__` dropping a
supplied config.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
oxy-giedrius
2026-09-08 10:12:48 +03:00
committed by GitHub
parent 7e18abd108
commit fe62d04cdb
12 changed files with 642 additions and 467 deletions

View File

@@ -4,7 +4,7 @@ description: >
تتيح أدوات استخراج Oxylabs الوصول بسهولة إلى المعلومات من المصادر المعنية. يرجى الاطلاع على قائمة المصادر المتاحة أدناه:
- `Amazon Product`
- `Amazon Search`
- `Google Seach`
- `Google Search`
- `Universal`
icon: globe
mode: "wide"
@@ -87,7 +87,7 @@ print(result)
### المعاملات
- `query` - مصطلح بحث Amazon.
- `domain` - توطين النطاق لـ Bestbuy.
- `domain` - توطين النطاق لـ Amazon.
- `start_page` - رقم صفحة البداية.
- `pages` - عدد الصفحات المراد استرجاعها.
- `geo_location` - موقع _التوصيل إلى_.

View File

@@ -4,7 +4,7 @@ description: >
Oxylabs Scrapers allow to easily access the information from the respective sources. Please see the list of available sources below:
- `Amazon Product`
- `Amazon Search`
- `Google Seach`
- `Google Search`
- `Universal`
icon: globe
mode: "wide"
@@ -87,7 +87,7 @@ print(result)
### Parameters
- `query` - Amazon search term.
- `domain` - Domain localization for Bestbuy.
- `domain` - domain localization for Amazon.
- `start_page` - starting page number.
- `pages` - number of pages to retrieve.
- `geo_location` - the _Deliver to_ location.

View File

@@ -4,7 +4,7 @@ description: >
Oxylabs 스크래퍼를 사용하면 해당 소스에서 정보를 쉽게 접근할 수 있습니다. 아래에서 사용 가능한 소스 목록을 확인하세요:
- `Amazon Product`
- `Amazon Search`
- `Google Seach`
- `Google Search`
- `Universal`
icon: globe
mode: "wide"
@@ -87,7 +87,7 @@ print(result)
### 파라미터
- `query` - Amazon 검색어.
- `domain` - Bestbuy의 도메인 로컬라이제이션.
- `domain` - Amazon의 도메인 로컬라이제이션.
- `start_page` - 시작 페이지 번호.
- `pages` - 가져올 페이지 수.
- `geo_location` - _배송지_ 위치.

View File

@@ -4,7 +4,7 @@ description: >
Os Scrapers da Oxylabs permitem acessar facilmente informações de fontes específicas. Veja abaixo a lista de fontes disponíveis:
- `Amazon Product`
- `Amazon Search`
- `Google Seach`
- `Google Search`
- `Universal`
icon: globe
mode: "wide"
@@ -87,7 +87,7 @@ print(result)
### Parâmetros
- `query` - termo de busca da Amazon.
- `domain` - Domínio de localização para Bestbuy.
- `domain` - domínio de localização da Amazon.
- `start_page` - número da página inicial.
- `pages` - quantidade de páginas a ser recuperada.
- `geo_location` - local de entrega (_Deliver to_).