Skip to content

About

OpenAI-compatible HTTP API for duck.ai (DuckDuckGo AI chat) — Go, with X-Vqd-Hash-1 PoW solver (in progress)

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Latest commit

 

History

12 Commits

Folders and files

Repository files navigation

duckai-bridge

OpenAI-compatible HTTP API for duck.ai (the free DuckDuckGo AI chat), written in Go. Built-in tool calling is supported out of the box (the bridge maps OpenAI tools[] onto duck.ai's WebSearch / GenerateImage / NewsSearch / etc. built-ins).

Status

✅ Working — end-to-end verified against https://duck.ai/ with model gpt-5.6-luna, including:

  • ✅ OpenAI-shaped POST /v1/chat/completions (streaming + non-streaming)
  • ✅ Tool calling: tools[] → duck.ai built-in WebSearch / RelatedSearchTerms (real web results, citations, tool_calls delta streaming)
  • ✅ GET /v1/models (gpt-5.6-luna, gpt-5.4-mini, claude-haiku-4-5, mistral-small-4, gpt-oss-120B, gemma-4-31B)
  • ✅ Non-tool chats (PONG-style smoke test passes)

How it works

  1. Browser capture (one-time per session): duck.ai's PoW is a JavaScript challenge that requires a real DOM (window.parent, document.querySelector, iframe.contentWindow.get, etc.) and crypto.subtle.digest('SHA-256', ...). No pure-Go library can run it. So the bridge does not solve the PoW itself; instead you paste in a pre-solved value captured from a real browser. The hash stays valid for several hours.
  2. Go client (internal/duckai): a thin *http.Client that sends POST /duckchat/v1/chat with the captured X-Vqd-Hash-1 and an RSA-OAEP-256 durableStream envelope, then parses the SSE stream.
  3. Go server (internal/server): OpenAI-compatible POST /v1/chat/completions. Translates OpenAI messages/tools/stream into duck.ai's request shape and translates the duck.ai SSE stream into OpenAI SSE chunks. tools[] names are matched (web_search, image, news, weather, etc.) and forwarded to duck.ai's metadata.toolChoice flags.

Capturing an X-Vqd-Hash-1

Option A — using the bundled Playwright snippet (recommended)

# 1. Install playwright
pip install playwright
playwright install chromium

# 2. Open https://duck.ai in Chromium and watch the network panel.
#    Fire any chat message. The /duckchat/v1/chat request will
#    carry an X-Vqd-Hash-1 header. Copy its value (it's a long
#    base64 string starting with "eyJ").

# 3. Save it:
echo "eyJ...==..." > /tmp/duckai_vqd.txt

Option B — using your existing browser

  1. Open https://duck.ai/, accept the page, type any message, hit send.
  2. Open DevTools → Network → find the chat request → right-click → Copy → Copy request headers.
  3. Extract the value of x-vqd-hash-1:. Save to a file.

Option C — run the included headless capture helper

# This requires playwright + an open chromium with --remote-debugging-port=9222
# See /tmp/duckai_capture/ in the development session for the
# worked example. The bridge does NOT bundle a browser to keep
# the binary small.

Running the bridge

go build -o duckai-bridge ./cmd/duckai-bridge

./duckai-bridge -listen :8788 -vqd-file /tmp/duckai_vqd.txt

# Or inline:
./duckai-bridge -listen :8788 -vqd "eyJ..."

# Or via env:
DUCKAIVQD="eyJ..." ./duckai-bridge -listen :8788

Endpoints

Method Path Notes
GET /healthz liveness
GET /v1/models OpenAI model list
POST /v1/chat/completions OpenAI chat (stream + non-stream + tool calls)

Example: smoke test

# Health
curl -s http://127.0.0.1:8788/healthz

# Models
curl -s http://127.0.0.1:8788/v1/models

# Non-streaming chat
curl -s -X POST http://127.0.0.1:8788/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-5.6-luna","messages":[{"role":"user","content":"Reply PONG only."}]}'

# Tool calling (the main feature)
curl -s -X POST http://127.0.0.1:8788/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.6-luna",
    "messages": [{"role": "user", "content": "Latest Python version?"}],
    "tools": [{"type": "function", "function": {"name": "web_search"}}]
  }'

Tool name mapping

OpenAI's tools[] carries user-defined function definitions. duck.ai only exposes a fixed set of BUILT-IN tools (it cannot run user-defined functions). The bridge translates by matching the function name (case-insensitive substring):

OpenAI function.name matches duck.ai metadata.toolChoice
web_search, websearch, google, anything ending in _search WebSearch: true
news, news_search NewsSearch: true
video, videos, video_search VideosSearch: true
local, local_search, place, maps LocalSearch: true
weather, forecast, get_weather WeatherForecast: true
image, generate_image, dalle, dall-e, create_image GenerateImage: true

Unknown names are silently dropped. If tool_choice names a specific tool the bridge enables only that one; if it can't be mapped, it falls back to WebSearch (the most common "I want the model to call something" intent).

Architecture

~/apps/duckai-bridge/
├── go.mod
├── cmd/duckai-bridge/
│   └── main.go               — flag/env parsing, signal handling
├── internal/
│   ├── vqd/                  — (legacy) JS PoW solver via goja; not used in
│   │   │                       the default path, kept for future use
│   │   ├── polyfills.go
│   │   ├── pow.go
│   │   └── pow_test.go
│   ├── duckai/
│   │   ├── client.go         — duck.ai HTTP client + SSE parser
│   │   └── jwk.go            — RSA priv-key reconstruction helper
│   └── server/
│       └── server.go         — OpenAI ↔ duck.ai translation, tool mapping
└── README.md

Limitations

  • No automatic VQD refresh. The X-Vqd-Hash-1 hash expires (typically after several hours of heavy use or a server-side rate limit). When the upstream starts returning 418 ERR_CHALLENGE you must capture a new hash. For a long-running deployment, wrap the bridge in a supervisor that re-captures the hash on 418 errors.
  • No custom user-defined tools. duck.ai only supports its built-in tool set; user-defined OpenAI tools[] are mapped to whichever built-in matches the function name.
  • No login / no auth. This is the public, unauthenticated duck.ai endpoint.

License

MIT.

About

OpenAI-compatible HTTP API for duck.ai (DuckDuckGo AI chat) — Go, with X-Vqd-Hash-1 PoW solver (in progress)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages