From 2ab51364b6762d36ebc2954b1c7ce2820b41d7bf Mon Sep 17 00:00:00 2001 From: Taylor Wilsdon Date: Wed, 23 Apr 2025 17:02:13 -0400 Subject: [PATCH 1/5] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3b8cdb9..dec70fc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# ๐ŸŒŸ OpenAPI Tool Servers +# ๐ŸŒŸ OpenAPI Tool Servers - Taylor's Fork + +This includes some that may not be merged in the main open-webui/openapi-servers repo This repository provides reference OpenAPI Tool Server implementations making it easy and secure for developers to integrate external tooling and data sources into LLM agents and workflows. Designed for maximum ease of use and minimal learning curve, these implementations utilize the widely adopted and battle-tested [OpenAPI specification](https://www.openapis.org/) as the standard protocol. From 1e69956afaa66b0c750d4404c02c925eacbaaefd Mon Sep 17 00:00:00 2001 From: Taylor Wilsdon Date: Wed, 23 Apr 2025 17:12:07 -0400 Subject: [PATCH 2/5] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dec70fc..5b6c4ee 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# ๐ŸŒŸ OpenAPI Tool Servers - Taylor's Fork +# taylorwilsdon's openapi-servers -This includes some that may not be merged in the main open-webui/openapi-servers repo +This is based on, but includes some that may not be merged in the main #open-webui/openapi-servers repo This repository provides reference OpenAPI Tool Server implementations making it easy and secure for developers to integrate external tooling and data sources into LLM agents and workflows. Designed for maximum ease of use and minimal learning curve, these implementations utilize the widely adopted and battle-tested [OpenAPI specification](https://www.openapis.org/) as the standard protocol. From ab5ac99502716b318ace771b854d2b60703bc3b9 Mon Sep 17 00:00:00 2001 From: Taylor Wilsdon Date: Wed, 23 Apr 2025 18:53:43 -0400 Subject: [PATCH 3/5] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5b6c4ee..787b305 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # taylorwilsdon's openapi-servers -This is based on, but includes some that may not be merged in the main #open-webui/openapi-servers repo +This is based on [open-webui/openapi-servers](https://github.com/open-webui/openapi-servers), but includes some tools that may not be merged in the main repo. Anything merged into main should be considered ready for use. This repository provides reference OpenAPI Tool Server implementations making it easy and secure for developers to integrate external tooling and data sources into LLM agents and workflows. Designed for maximum ease of use and minimal learning curve, these implementations utilize the widely adopted and battle-tested [OpenAPI specification](https://www.openapis.org/) as the standard protocol. From 089ecbdbbdee9d320aa31374312bae28c673de72 Mon Sep 17 00:00:00 2001 From: Taylor Wilsdon Date: Thu, 17 Jul 2025 15:04:07 -0400 Subject: [PATCH 4/5] add Google PSE OpenAPI tool server --- servers/google-pse/Dockerfile | 51 +++++++++ servers/google-pse/README.md | 154 +++++++++++++++++++++++++ servers/google-pse/compose.yaml | 6 + servers/google-pse/main.py | 172 ++++++++++++++++++++++++++++ servers/google-pse/requirements.txt | 5 + 5 files changed, 388 insertions(+) create mode 100644 servers/google-pse/Dockerfile create mode 100644 servers/google-pse/README.md create mode 100644 servers/google-pse/compose.yaml create mode 100644 servers/google-pse/main.py create mode 100644 servers/google-pse/requirements.txt diff --git a/servers/google-pse/Dockerfile b/servers/google-pse/Dockerfile new file mode 100644 index 0000000..ab20c1f --- /dev/null +++ b/servers/google-pse/Dockerfile @@ -0,0 +1,51 @@ +# syntax=docker/dockerfile:1 + +# Comments are provided throughout this file to help you get started. +# If you need more help, visit the Dockerfile reference guide at +# https://docs.docker.com/go/dockerfile-reference/ + +# Want to help us make this template better? Share your feedback here: https://forms.gle/ybq9Krt8jtBL3iCk7 + +ARG PYTHON_VERSION=3.10.12 +FROM python:${PYTHON_VERSION}-slim as base + +# Prevents Python from writing pyc files. +ENV PYTHONDONTWRITEBYTECODE=1 + +# Keeps Python from buffering stdout and stderr to avoid situations where +# the application crashes without emitting any logs due to buffering. +ENV PYTHONUNBUFFERED=1 + +WORKDIR /app + +# Create a non-privileged user that the app will run under. +# See https://docs.docker.com/go/dockerfile-user-best-practices/ +ARG UID=10001 +RUN adduser \ + --disabled-password \ + --gecos "" \ + --home "/nonexistent" \ + --shell "/sbin/nologin" \ + --no-create-home \ + --uid "${UID}" \ + appuser + +# Download dependencies as a separate step to take advantage of Docker's caching. +# Leverage a cache mount to /root/.cache/pip to speed up subsequent builds. +# Leverage a bind mount to requirements.txt to avoid having to copy them into +# into this layer. +RUN --mount=type=cache,target=/root/.cache/pip \ + --mount=type=bind,source=requirements.txt,target=requirements.txt \ + python -m pip install -r requirements.txt + +# Switch to the non-privileged user to run the application. +USER appuser + +# Copy the source code into the container. +COPY . . + +# Expose the port that the application listens on. +EXPOSE 8000 + +# Run the application. +CMD uvicorn 'main:app' --host=0.0.0.0 --port=8000 \ No newline at end of file diff --git a/servers/google-pse/README.md b/servers/google-pse/README.md new file mode 100644 index 0000000..0c2be7d --- /dev/null +++ b/servers/google-pse/README.md @@ -0,0 +1,154 @@ +# ๐Ÿ” Google PSE Tool Server + +A sleek and simple FastAPI-based server to provide web search functionality using Google's Programmable Search Engine (PSE) via the Custom Search JSON API. + +๐Ÿ“ฆ Built with: +โšก๏ธ FastAPI โ€ข ๐Ÿ“œ OpenAPI โ€ข ๐Ÿงฐ Python โ€ข ๐Ÿ” Google Custom Search API + +--- + +## ๐Ÿš€ Quickstart + +Clone the repo and get started in seconds: + +```bash +git clone https://github.com/open-webui/openapi-servers +cd openapi-servers/servers/google-pse + +# Install dependencies +pip install -r requirements.txt + +# Set up environment variables +export GOOGLE_API_KEY="your_google_api_key_here" +export GOOGLE_PSE_CX="your_search_engine_id_here" + +# Run the server +uvicorn main:app --host 0.0.0.0 --reload +``` + +--- + +## ๐Ÿ”ง Configuration + +### Required Environment Variables + +- `GOOGLE_API_KEY`: Your Google API key with Custom Search API enabled +- `GOOGLE_PSE_CX`: Your Programmable Search Engine ID + +### Getting Your Credentials + +1. **Google API Key**: + - Go to the [Google Cloud Console](https://console.cloud.google.com/) + - Create a new project or select an existing one + - Enable the Custom Search JSON API + - Create credentials (API Key) + +2. **Programmable Search Engine ID**: + - Visit [Google Programmable Search Engine](https://programmablesearchengine.google.com/) + - Create a new search engine + - Copy the Search Engine ID (cx parameter) + +--- + +## ๐Ÿ” About + +This server is part of the OpenAPI Tools Collection. Use it to perform web searches programmatically, retrieve search results with metadata, and integrate search functionality into your applications โ€” all wrapped in a developer-friendly OpenAPI interface. + +### Features + +- ๐ŸŒ Web search using Google's Custom Search JSON API +- ๐ŸŽฏ Advanced search parameters (language, country, date restrictions, etc.) +- ๐Ÿ“Š Rich metadata including search timing and result counts +- ๐Ÿ”’ Configurable safe search filtering +- ๐Ÿ“„ Support for file type and site-specific searches +- ๐Ÿš€ Fast and reliable RESTful API + +Compatible with any OpenAPI-supported ecosystem, including: + +- ๐ŸŒ€ FastAPI +- ๐Ÿ“˜ Swagger UI +- ๐Ÿงช API testing tools +- ๐Ÿค– AI agents and LLMs + +--- + +## ๐Ÿ“ก API Endpoints + +### `GET /search` + +Perform a web search with comprehensive parameters: + +**Required Parameters:** +- `q`: Search query string + +**Optional Parameters:** +- `num`: Number of results (1-10, default: 10) +- `start`: Starting index for results (default: 1) +- `safe`: Safe search level ('active' or 'off') +- `lr`: Language restriction (e.g., 'lang_en') +- `cr`: Country restriction (e.g., 'countryUS') +- `dateRestrict`: Date restriction ('d1', 'w1', 'm1', 'y1') +- `exactTerms`: Phrase that must appear in all results +- `excludeTerms`: Terms to exclude from results +- `fileType`: File type filter ('pdf', 'doc', etc.) +- `siteSearch`: Specific site to search +- `siteSearchFilter`: Include ('i') or exclude ('e') site + +### `GET /health` + +Health check endpoint to verify service status. + +--- + +## ๐Ÿšง Customization + +Extend the search functionality, add custom filters, or integrate with other APIs. Perfect for: + +- ๐Ÿค– AI agent web research capabilities +- ๐Ÿ“Š Automated content discovery +- ๐Ÿ” Custom search applications +- ๐Ÿ“ˆ SEO and market research tools + +--- + +## ๐ŸŒ API Documentation + +Once running, explore auto-generated interactive docs: + +๐Ÿ–ฅ๏ธ Swagger UI: http://localhost:8000/docs +๐Ÿ“„ OpenAPI JSON: http://localhost:8000/openapi.json + +--- + +## ๐Ÿณ Docker Support + +Run with Docker Compose: + +```bash +# Set environment variables in your shell or .env file +export GOOGLE_API_KEY="your_api_key" +export GOOGLE_PSE_CX="your_search_engine_id" + +# Start the service +docker-compose up --build +``` + +--- + +## ๐Ÿ“ Example Usage + +```bash +# Basic search +curl "http://localhost:8000/search?q=OpenAI+GPT" + +# Advanced search with filters +curl "http://localhost:8000/search?q=machine+learning&num=5&lr=lang_en&dateRestrict=m1" + +# Site-specific search +curl "http://localhost:8000/search?q=python+tutorial&siteSearch=github.com&siteSearchFilter=i" +``` + +--- + +Made with โค๏ธ by the Open WebUI community ๐ŸŒ +Explore more tools โžก๏ธ https://github.com/open-webui/openapi-servers \ No newline at end of file diff --git a/servers/google-pse/compose.yaml b/servers/google-pse/compose.yaml new file mode 100644 index 0000000..9bcfb67 --- /dev/null +++ b/servers/google-pse/compose.yaml @@ -0,0 +1,6 @@ +services: + server: + build: + context: . + ports: + - 8000:8000 \ No newline at end of file diff --git a/servers/google-pse/main.py b/servers/google-pse/main.py new file mode 100644 index 0000000..5bf1935 --- /dev/null +++ b/servers/google-pse/main.py @@ -0,0 +1,172 @@ +import os +import requests +from fastapi import FastAPI, HTTPException, Query +from fastapi.middleware.cors import CORSMiddleware +from pydantic import BaseModel, Field +from typing import Optional, List + +app = FastAPI( + title="Google Programmable Search Engine API", + version="1.0.0", + description="Provides web search functionality using Google's Custom Search JSON API.", +) + +origins = ["*"] + +app.add_middleware( + CORSMiddleware, + allow_origins=origins, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# ------------------------------- +# Pydantic models +# ------------------------------- + +class SearchItem(BaseModel): + title: str = Field(..., description="The title of the search result") + link: str = Field(..., description="The URL of the search result") + snippet: str = Field(..., description="A snippet of text from the search result") + displayLink: Optional[str] = Field(None, description="The display URL") + formattedUrl: Optional[str] = Field(None, description="The formatted URL") + +class SearchInformation(BaseModel): + searchTime: float = Field(..., description="The time taken to perform the search") + formattedSearchTime: str = Field(..., description="The formatted search time") + totalResults: str = Field(..., description="The total number of search results") + formattedTotalResults: str = Field(..., description="The formatted total results") + +class Queries(BaseModel): + request: Optional[List[dict]] = Field(None, description="The request query parameters") + nextPage: Optional[List[dict]] = Field(None, description="Parameters for the next page") + previousPage: Optional[List[dict]] = Field(None, description="Parameters for the previous page") + +class Context(BaseModel): + title: str = Field(..., description="The title of the custom search engine") + +class SearchResponse(BaseModel): + kind: str = Field(..., description="The kind of search response") + url: dict = Field(..., description="URL information") + queries: Queries = Field(..., description="Query information") + context: Context = Field(..., description="Search engine context") + searchInformation: SearchInformation = Field(..., description="Information about the search") + items: Optional[List[SearchItem]] = Field(None, description="The search results") + +# ------------------------------- +# Routes +# ------------------------------- + +GOOGLE_SEARCH_URL = "https://customsearch.googleapis.com/customsearch/v1" + +@app.get("/search", response_model=SearchResponse, summary="Perform a web search") +def search_web( + q: str = Query(..., description="The search query"), + cx: Optional[str] = Query(None, description="The Programmable Search Engine ID (can be set via GOOGLE_PSE_CX env var)"), + api_key: Optional[str] = Query(None, description="Google API key (can be set via GOOGLE_API_KEY env var)"), + num: Optional[int] = Query(10, ge=1, le=10, description="Number of search results to return (1-10)"), + start: Optional[int] = Query(1, ge=1, le=91, description="The index of the first result to return"), + safe: Optional[str] = Query("off", description="Search safety level: 'active' or 'off'"), + lr: Optional[str] = Query(None, description="Language restriction (e.g., 'lang_en')"), + cr: Optional[str] = Query(None, description="Country restriction (e.g., 'countryUS')"), + dateRestrict: Optional[str] = Query(None, description="Date restriction (e.g., 'd1', 'w1', 'm1', 'y1')"), + exactTerms: Optional[str] = Query(None, description="Phrase that all results must contain"), + excludeTerms: Optional[str] = Query(None, description="Terms to exclude from results"), + fileType: Optional[str] = Query(None, description="File type restriction (e.g., 'pdf', 'doc')"), + siteSearch: Optional[str] = Query(None, description="Site to search within or exclude"), + siteSearchFilter: Optional[str] = Query(None, description="Include ('i') or exclude ('e') siteSearch") +): + """ + Performs a web search using Google's Custom Search JSON API. + + The API key and search engine ID can be provided as query parameters or set as environment variables: + - GOOGLE_API_KEY: Your Google API key + - GOOGLE_PSE_CX: Your Programmable Search Engine ID + + Returns search results in JSON format with metadata about the search. + """ + + # Get API key and search engine ID from parameters or environment variables + google_api_key = api_key or os.getenv("GOOGLE_API_KEY") + search_engine_id = cx or os.getenv("GOOGLE_PSE_CX") + + if not google_api_key: + raise HTTPException( + status_code=400, + detail="Google API key is required. Provide it as 'api_key' parameter or set GOOGLE_API_KEY environment variable." + ) + + if not search_engine_id: + raise HTTPException( + status_code=400, + detail="Programmable Search Engine ID is required. Provide it as 'cx' parameter or set GOOGLE_PSE_CX environment variable." + ) + + # Build request parameters + params = { + "key": google_api_key, + "cx": search_engine_id, + "q": q, + "num": num, + "start": start, + "safe": safe + } + + # Add optional parameters if provided + optional_params = { + "lr": lr, + "cr": cr, + "dateRestrict": dateRestrict, + "exactTerms": exactTerms, + "excludeTerms": excludeTerms, + "fileType": fileType, + "siteSearch": siteSearch, + "siteSearchFilter": siteSearchFilter + } + + for key, value in optional_params.items(): + if value is not None: + params[key] = value + + try: + response = requests.get(GOOGLE_SEARCH_URL, params=params) + response.raise_for_status() + data = response.json() + + # Check if the response contains an error + if "error" in data: + error_info = data["error"] + raise HTTPException( + status_code=error_info.get("code", 400), + detail=f"Google API Error: {error_info.get('message', 'Unknown error')}" + ) + + # Validate required fields are present + required_fields = ["kind", "url", "queries", "context", "searchInformation"] + for field in required_fields: + if field not in data: + raise HTTPException( + status_code=500, + detail=f"Unexpected response format from Google API: missing '{field}'" + ) + + return data + + except requests.exceptions.RequestException as e: + raise HTTPException( + status_code=503, + detail=f"Error connecting to Google Custom Search API: {e}" + ) + except Exception as e: + raise HTTPException( + status_code=500, + detail=f"An internal error occurred: {e}" + ) + +@app.get("/health", summary="Health check endpoint") +def health_check(): + """ + Simple health check endpoint to verify the service is running. + """ + return {"status": "healthy", "service": "google-pse"} \ No newline at end of file diff --git a/servers/google-pse/requirements.txt b/servers/google-pse/requirements.txt new file mode 100644 index 0000000..4dd36c6 --- /dev/null +++ b/servers/google-pse/requirements.txt @@ -0,0 +1,5 @@ +fastapi +uvicorn[standard] +pydantic +python-multipart +requests \ No newline at end of file From 549ed0803b9b27bef70cc53012e23b7e4f641ae1 Mon Sep 17 00:00:00 2001 From: Taylor Wilsdon Date: Thu, 17 Jul 2025 15:06:02 -0400 Subject: [PATCH 5/5] revert readme change --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 787b305..3b8cdb9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,4 @@ -# taylorwilsdon's openapi-servers - -This is based on [open-webui/openapi-servers](https://github.com/open-webui/openapi-servers), but includes some tools that may not be merged in the main repo. Anything merged into main should be considered ready for use. +# ๐ŸŒŸ OpenAPI Tool Servers This repository provides reference OpenAPI Tool Server implementations making it easy and secure for developers to integrate external tooling and data sources into LLM agents and workflows. Designed for maximum ease of use and minimal learning curve, these implementations utilize the widely adopted and battle-tested [OpenAPI specification](https://www.openapis.org/) as the standard protocol.