Skip to main content

Async HTTP client for the Unique Search Proxy API

Project description

unique-search-proxy-sdk

Part of Unique Search Proxy · PyPI: unique-search-proxy-sdk


1. What this package is

The SDK is the caller's HTTP client. Platform services (e.g. assistants-core) use it to reach the proxy pod at runtime — search, agent search, and crawl — without hand-crafting httpx calls or duplicating route paths.

It depends on core for request validation and typed errors. It does not include the FastAPI server.

Package Question it answers
Core What can be configured and what does a valid request/response look like?
Client How are provider calls executed at runtime?
SDK (this) How do callers reach the proxy over HTTP?

2. Role in the system

The SDK implements Path B (runtime HTTP) from the system overview. Schema work (Path A) stays in core — the SDK does not expose deployment config or LLM call-schema endpoints.

flowchart LR
    Caller["Caller service"]
    SDK["unique_search_proxy_sdk"]
    Core["unique_search_proxy_core"]
    Proxy["unique_search_proxy_client"]

    Caller --> SDK
    SDK --> Core
    SDK -->|"httpx"| Proxy

Before a search call, the caller (or SDK) validates the payload against core request models. Non-2xx responses become the same ProxyError subclasses defined in core.


3. Architecture

Two layers: a hand-written facade over OpenAPI-generated code.

flowchart TB
    subgraph sdk_pkg["unique_search_proxy_sdk"]
        Facade["UniqueSearchProxyClient"]
        SC["SearchClient"]
        ASC["AgentSearchClient"]
        CC["CrawlClient"]
        Transport["OpenapiTransport"]
        Gen["_generated/"]
        Conv["converters.py"]
        Err["errors.py"]
    end

    Core["unique_search_proxy_core"]
    Proxy["Proxy pod"]

    Facade --> SC & ASC & CC
    SC & ASC & CC --> Transport
    Transport --> Gen
    SC --> Conv --> Core
    Err --> Core
    Gen -->|"httpx AsyncClient"| Proxy
Layer Path Responsibility
Facade client.py UniqueSearchProxyClient — composes sub-clients, health(), ready()
Sub-clients search_client.py, agent_search_client.py, crawl_client.py Per-provider typed endpoints + compatibility dispatchers
Endpoint factory _endpoint.py async_post_endpoint / async_sse_endpoint (ParamSpec from core request models)
Transport _transport.py, _http.py Shared httpx lifecycle, base URL, timeout
Generated _generated/ Route functions + attrs models from OpenAPI (do not edit)
Converters converters.py Core Pydantic models → generated SDK models
Errors errors.py Error envelope → core ProxyError subclasses

The client package is the source of truth for the HTTP contract. When routes change, regenerate _generated/ from unique_search_proxy_client/openapi.json.


4. Usage

Each provider exposes a typed endpoint whose kwargs mirror the core *Request Pydantic model (via ParamSpec). Snake_case and camelCase aliases both work (fetch_size / fetchSize).

from unique_search_proxy_sdk import UniqueSearchProxyClient

async with UniqueSearchProxyClient("http://unique-search-proxy:2349") as client:
    await client.health()

    # Typed per-provider endpoints (preferred)
    result = await client.search.google(query="unique ag", gl="ch", fetch_size=10)
    brave = await client.search.brave(query="news", country="US", safesearch="strict")
    agent = await client.agent_search.bing(query="EU AI Act timeline", fetch_size=5)
    crawl = await client.crawl.basic(urls=["https://example.com"])

    # Compatibility dispatchers (engine/crawler string)
    result = await client.search.search("unique ag", engine="google", fetch_size=10)
    agent = await client.agent_search.search("query", engine="bing")
    crawl = await client.crawl.crawl(["https://example.com"], crawler="Basic")

    # Agent SSE streaming
    async for event in client.agent_search.bing_stream(query="query"):
        ...

    # Low-level: one generated function per route
    raw_client = client.openapi

Search payloads are validated through core's parse_search_request() before serialization. Agent and crawl clients follow the same pattern.


5. API mapping

Facade method HTTP
health() GET /health
ready() GET /ready
search.google(...) / .brave(...) / .perplexity(...) POST /v1/search
search.search(...) POST /v1/search (dispatcher)
agent_search.bing(...) / .vertexai(...) POST /v1/agent-search
agent_search.bing_stream(...) / .vertexai_stream(...) POST /v1/agent-search/stream (SSE)
agent_search.search(...) / .stream(...) same routes (dispatchers)
crawl.basic(...) / .tavily(...) / .jina(...) / .firecrawl(...) POST /v1/crawl
crawl.crawl(...) POST /v1/crawl (dispatcher)
openapi Low-level generated client (one function per route)

Endpoint payloads and provider ids → Client README.


6. Error handling

Non-success responses with a proxy error envelope raise typed exceptions from core:

from unique_search_proxy_core import EngineNotConfiguredError, UpstreamError

ENGINE_NOT_CONFIGURED (503) includes missing_env_vars parsed from the error details list — useful for operators and LLM tool consumers.

Transport failures (non-JSON body, connection errors) raise UniqueSearchProxyClientError.


7. Codegen workflow

Generated via openapi-python-client from the server's OpenAPI spec.

cd ../unique_search_proxy_client
uv sync
uv run python scripts/generate_sdk.py
Path Role
unique_search_proxy_sdk/_generated/ Regenerated httpx client + attrs models
unique_search_proxy_client/openapi.json Exported spec (codegen input)
unique_search_proxy_sdk/client.py Hand-written facade (not regenerated)

Other codegen tools considered: OpenAPI Generator, datamodel-code-generator, Kiota — see git history in the client README if needed.


8. Testing

Pass an injected httpx.AsyncClient with ASGITransport for in-process tests against create_app():

import httpx
from httpx import ASGITransport
from unique_search_proxy_client.web.app import create_app
from unique_search_proxy_sdk import UniqueSearchProxyClient

app = create_app()
async with httpx.AsyncClient(
    transport=ASGITransport(app=app),
    base_url="http://test",
) as http:
    async with UniqueSearchProxyClient("http://test", http_client=http) as client:
        ...

Run the app lifespan so the in-process HttpClientPool is initialised.


9. Installation & development

cd unique_search_proxy_sdk
uv sync
uv run pytest
uv run ruff check .
uv run basedpyright

Depends on unique-search-proxy-core. Does not depend on unique-search-proxy at runtime (only as a dev dependency for integration tests).


License

Proprietary — Unique AG

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

unique_search_proxy_sdk-2026.26.0.dev5.tar.gz (36.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

File details

Details for the file unique_search_proxy_sdk-2026.26.0.dev5.tar.gz.

File metadata

  • Download URL: unique_search_proxy_sdk-2026.26.0.dev5.tar.gz
  • Upload date:
  • Size: 36.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.5 {"installer":{"name":"uv","version":"0.11.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for unique_search_proxy_sdk-2026.26.0.dev5.tar.gz
Algorithm Hash digest
SHA256 d1aad101784d26db45b5256bd4254acc50440d7081d1a44f416bc0d5391ce3c0
MD5 06699d2e8b44ae11d3071f04bea83fe8
BLAKE2b-256 0e9fb70843db6368a542003724e87d82564366aebcc27adb5c0fb35c3d5612d4

See more details on using hashes here.

File details

Details for the file unique_search_proxy_sdk-2026.26.0.dev5-py3-none-any.whl.

File metadata

  • Download URL: unique_search_proxy_sdk-2026.26.0.dev5-py3-none-any.whl
  • Upload date:
  • Size: 72.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.5 {"installer":{"name":"uv","version":"0.11.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for unique_search_proxy_sdk-2026.26.0.dev5-py3-none-any.whl
Algorithm Hash digest
SHA256 89a832a02725b970c063a6b6d196470d1823510562a2007bbe97e2c86ce4f443
MD5 61dff93f61eea1dcde19453ff04a345a
BLAKE2b-256 a8399e008beda5e8758d6ffb704620cc0e9c583bf68ea9d23e98be00fb860cae

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page