Skip to main content

Web Search Proxy implementation

Project description

unique-search-proxy (client)

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


1. What this package is

The client is the proxy pod. It is the only deployable service in the stack — a FastAPI application that owns secrets, outbound networking, provider adapters, and observability.

Platform services do not talk to Google or Tavily directly. They call this server's HTTP API (usually via the SDK). Shared types and config models come from core.

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

2. Role in the system

flowchart LR
    SDK["unique_search_proxy_sdk"]
    Client["unique_search_proxy_client\n(this package)"]
    Core["unique_search_proxy_core"]
    Pool["HttpClientPool"]
    Providers["Google · Brave · Bing · …"]

    SDK -->|"POST /v1/*"| Client
    Client --> Core
    Client --> Pool
    Pool --> Providers
    Client --> Providers

System overview → ../README.md

What the client owns: HTTP routes, provider registry, credential loading, httpx egress pool, Prometheus, Docker/Helm deployment.

What it does not own: Deployment config JSON Schema, LLM call-schema projection, config/invocation merge — those live in core and run in caller processes.


3. Internal architecture

Requests flow through four layers inside the FastAPI app:

flowchart TB
    subgraph client_pkg["unique_search_proxy_client.web"]
        API["api/ — route handlers"]
        Reg["core/registry.py — provider lookup"]
        Svc["core/*/service.py — provider adapters"]
        Pool["core/client/ — HttpClientPool"]
        Settings["settings/ — env & secrets"]
        Mon["monitoring/ — Prometheus"]
    end

    API --> Reg
    Reg --> Svc
    Svc --> Pool
    Svc --> Settings
    API --> Mon
Layer Path Responsibility
Entry app.py App factory, lifespan (start/stop pool), router mount
API api/health.py, api/v1/*.py Validate body via core models, dispatch, record metrics
Registry core/registry.py, core/providers.py Register built-in engines/crawlers at startup
Services core/search_engines/, core/agent_engines/, core/crawlers/ Provider-specific HTTP/SDK calls, response curation
Egress core/client/service.py Shared httpx client (optional corporate proxy / mTLS)
Settings settings/providers/*, settings/client.py Per-provider credentials; unset → NOT_PROVIDED → 503
Monitoring monitoring/ Search / agent / crawl latency and error counters
Deploy deploy/ Dockerfile, Helm chart, uvicorn entrypoint

Built-in providers register in register_builtin_providers() during create_app().


4. HTTP API

Endpoint Description
GET /health Liveness
GET /ready Readiness (httpx pool + registered providers)
GET /v1/configuration/providers Registered search engine, agent engine, and crawler ids
POST /v1/search Standard search (flat body: engine, query, provider params, timeout)
POST /v1/agent-search Grounded agent search — opaque answer + raw
POST /v1/agent-search/stream Same, streamed as SSE (delta + done)
POST /v1/crawl Crawl URLs (flat body: crawler, urls, timeout, …)
GET /metrics Prometheus (when enabled)
/docs Swagger UI with preset examples

OpenAPI spec: openapi.json (input for SDK codegen).


5. Providers

5.1 Summary

Provider Type Upstream Credentials
Google Search Custom Search JSON API GOOGLE_SEARCH_API_KEY, GOOGLE_SEARCH_ENGINE_ID
Brave Search Brave Search API BRAVE_SEARCH_API_KEY
Perplexity Search Perplexity API PERPLEXITY_SEARCH_API_KEY
Bing Agent Azure AI Projects grounding BING_AGENT_*
VertexAI Agent Google GenAI + grounding VERTEXAI_AGENT_* or ADC
Basic Crawl Direct httpx + HTML/PDF processors (none)
Tavily Crawl Tavily extract API TAVILY_API_KEY
Jina Crawl Jina Reader API JINA_API_KEY
Firecrawl Crawl Firecrawl scrape (with polling) FIRECRAWL_API_KEY

Unconfigured providers return 503 ENGINE_NOT_CONFIGURED with missing env var names. GET /v1/configuration/providers lists what is registered in the running pod.

5.2 Search (POST /v1/search)

Flat request — tooling merges deployment config with LLM args in core before calling the proxy:

{
  "engine": "google",
  "query": "example query",
  "fetchSize": 10,
  "gl": "de",
  "dateRestrict": "d7",
  "timeout": 30
}

Response includes curated normalised results and opaque raw provider payload:

{
  "engine": "google",
  "query": "example query",
  "raw": { "pages": [{ "pageIndex": 1, "response": {} }] },
  "curated": [
    { "url": "https://example.com", "title": "Example", "snippet": "...", "content": "" }
  ]
}

5.3 Agent search (POST /v1/agent-search)

Thin egress — proxy returns opaque agent text; callers own parsing and citation extraction:

{
  "engine": "bing",
  "query": "latest EU AI Act timeline",
  "generationInstructions": "...",
  "fetchSize": 5,
  "timeout": 120
}

Streaming (/v1/agent-search/stream) emits SSE { "type": "delta", "text": "..." } chunks and a terminal { "type": "done", "response": { … } }.

5.4 Crawl (POST /v1/crawl)

Per-URL outcomes — one URL failing does not fail the batch:

{
  "urls": ["https://example.com"],
  "crawler": "Basic",
  "timeout": 30
}

Crawler discriminators: Basic, Tavily, Jina, Firecrawl.

5.5 Errors

Structured envelope on all non-2xx responses:

{
  "error": {
    "code": "ENGINE_NOT_CONFIGURED",
    "message": "Provider is not configured. Set environment variable(s): GOOGLE_SEARCH_API_KEY",
    "retryable": false
  }
}

Error types are defined in core and raised by the SDK on the caller side.


6. Configuration

Settings use pydantic-settings with per-provider env vars. Copy .env.example to .env for an annotated template.

Component Prefix / vars Example
Google search (none) GOOGLE_SEARCH_API_KEY, GOOGLE_SEARCH_ENGINE_ID
Brave search (none) BRAVE_SEARCH_API_KEY, BRAVE_SEARCH_API_ENDPOINT
Perplexity (none) PERPLEXITY_SEARCH_API_KEY
Tavily TAVILY_ TAVILY_API_KEY
Jina JINA_ JINA_API_KEY, JINA_DEPLOYMENT
Firecrawl FIRECRAWL_ FIRECRAWL_API_KEY, FIRECRAWL_API_VERSION
Bing agent BING_AGENT_ BING_AGENT_ENDPOINT, BING_AGENT_BING_RESOURCE_CONNECTION_STRING
VertexAI agent VERTEXAI_AGENT_ VERTEXAI_AGENT_SERVICE_ACCOUNT_CREDENTIALS (optional)
HTTP client HTTP_CLIENT_ HTTP_CLIENT_PROXY_HOST, HTTP_CLIENT_POOL_TIMEOUT_SECONDS
Prometheus PROMETHEUS_ PROMETHEUS_ENABLED
Container (shell) HOST, PORT, WORKERS, LOG_LEVEL

With WORKERS > 1, the entrypoint sets PROMETHEUS_MULTIPROC_DIR for correct metric aggregation.


7. Quick start

Prerequisites: Python 3.12+, uv

uv sync
cp .env.example .env
# Edit .env: set GOOGLE_SEARCH_API_KEY and GOOGLE_SEARCH_ENGINE_ID for live /v1/search
uv run python -m unique_search_proxy_client.web.app
# or
uv run uvicorn unique_search_proxy_client.web.app:app --reload --port 2349

Call from Python via the SDK:

from unique_search_proxy_sdk import UniqueSearchProxyClient

async with UniqueSearchProxyClient("http://localhost:2349") as client:
    result = await client.search.search("unique ag", engine="google", fetchSize=10)

Regenerate SDK after API changes:

uv run python scripts/generate_sdk.py

8. Dev testing

  1. Start the server and configure .env.
  2. Swagger/docsTry it out on /v1/search or /v1/crawl → pick an Examples preset.
  3. CLI — same presets from the terminal:
uv run python scripts/try_presets.py list
uv run python scripts/try_presets.py run google_minimal
uv run python scripts/try_presets.py run-all --kind crawl
uv run python scripts/try_presets.py run google_minimal --base-url http://127.0.0.1:8080

Presets live in web/presets/ (shared with Swagger examples). Add --strict to exit non-zero on any non-2xx response.


9. Project structure

unique_search_proxy_client/
├── openapi.json              # Exported spec (SDK codegen input)
├── scripts/
│   ├── generate_sdk.py
│   └── try_presets.py
├── deploy/                   # Dockerfile, Helm chart
├── tests/
└── unique_search_proxy_client/web/
    ├── app.py
    ├── api/                  # health + v1 routes
    ├── core/                 # registry, services, HttpClientPool
    ├── settings/             # env & provider credentials
    ├── monitoring/
    └── presets/

10. Development

uv run ruff check .
uv run ruff format .
uv run pytest
uv run basedpyright

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-2026.28.0.dev4.tar.gz (56.4 kB view details)

Uploaded Source

Built Distribution

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

unique_search_proxy-2026.28.0.dev4-py3-none-any.whl (109.7 kB view details)

Uploaded Python 3

File details

Details for the file unique_search_proxy-2026.28.0.dev4.tar.gz.

File metadata

  • Download URL: unique_search_proxy-2026.28.0.dev4.tar.gz
  • Upload date:
  • Size: 56.4 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-2026.28.0.dev4.tar.gz
Algorithm Hash digest
SHA256 c907174a47e40309d4fcedb9c1d0ac6eda31a0c036ef7aa076b88771ccfe1a66
MD5 e56369ca3f5d1a2417f8f580a9343098
BLAKE2b-256 430da1b52e8803ad4b857145bc1c51424ed32cce803f0dfb5eec17f3b5bb25e3

See more details on using hashes here.

File details

Details for the file unique_search_proxy-2026.28.0.dev4-py3-none-any.whl.

File metadata

  • Download URL: unique_search_proxy-2026.28.0.dev4-py3-none-any.whl
  • Upload date:
  • Size: 109.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-2026.28.0.dev4-py3-none-any.whl
Algorithm Hash digest
SHA256 6e6e5abd14188eb27987b903526649f1d5a42d39fa7de43847ac296224309f1d
MD5 f688d537ccc2d27d81bd2494bb7a3deb
BLAKE2b-256 ec10cbee66311eb871fcce217f87cdfdfb958c71140388bf092d3a1f9712b2c5

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