2026-09-07 08:02:09 +03:00
# gr2gw: Universal Gradio to OpenAI LLM gateway
2026-09-07 07:45:50 +03:00
A zero-dependency, high-performance Go proxy server that introspects any Gradio chat space (such as Hugging Face Spaces or custom deployments) and exposes a standards-compliant OpenAI `/v1/chat/completions` and `/v1/models` HTTP API.
Default demo space: `https://ghost2513-openai-gpt-oss-120b.hf.space`
## Features
2026-09-07 08:02:09 +03:00
- **Zero external dependencies**: Pure Go standard library (`net/http` , `encoding/json` , `bufio` , etc.).
- **Automatic space introspection**: Dynamically queries `/gradio_api/info` , `/config` , and Hugging Face space metadata to discover models, endpoints, and input parameter mappings.
- **Native Tencent Hunyuan 3 (`tencent-hy3` ) support**:
2026-09-07 07:56:39 +03:00
- Full native zero-degradation handling for official spaces like `https://tencent-hy3.hf.space` .
- Maps `functions_json_str` natively without polluting the system prompt.
- Preserves multi-turn reasoning content and tool call history in standard OpenAI message schemas.
- Maps `reasoning_effort` (`no_think` , `low` , `high` ) directly to `think_level` .
2026-09-07 08:02:09 +03:00
- **Universal multi-turn handling**:
2026-09-07 07:45:50 +03:00
- Automatically formats conversation history into structured inputs when the space supports them.
- Transparently composes multi-turn dialogue (`System` , `User` , `Assistant` ) into single prompt inputs when the space only accepts a single message textbox.
- Automatically pads hidden/State inputs (e.g. Gradio State components) to prevent backend argument count mismatches.
2026-09-07 08:02:09 +03:00
- **Real-time streaming & accumulation filter**:
2026-09-07 07:56:39 +03:00
- Automatically computes token deltas from cumulative or incremental Gradio SSE output streams (including 2D Hy3 frames `[[content, reasoning, tool_calls, history]]` ).
2026-09-07 07:45:50 +03:00
- Emits standards-compliant `chat.completion.chunk` SSE events in real time.
2026-09-07 08:02:09 +03:00
- **Thinking & reasoning token separation**:
2026-09-07 07:56:39 +03:00
- Streams native reasoning chunks as `delta.reasoning_content` in real time.
- Detects `<think>...</think>` tags in real time as fallback for standard spaces.
2026-09-07 07:45:50 +03:00
- Separates reasoning into `delta.reasoning_content` (streaming) and `message.reasoning_content` (non-streaming).
- Keeps `content` clean without tag leakage.
2026-09-07 08:02:09 +03:00
- **Full tool calling & function interception**:
2026-09-07 07:56:39 +03:00
- Formats schemas into native `functions_json_str` (Hy3) or system prompts (standard spaces).
2026-09-07 07:45:50 +03:00
- **`StreamToolCallFilter` **: Stateful sliding-window filter that prevents `<tool_call>` tags from leaking into `delta.content` . Emits structured OpenAI `delta.tool_calls` chunks and sets `finish_reason: "tool_calls"` .
- Seamlessly maintains multi-turn context when tool results are submitted back via `role: "tool"` .
2026-09-07 08:02:09 +03:00
- **Built-in SOCKS5 proxy client**:
2026-09-07 07:45:50 +03:00
- Full RFC 1928 / RFC 1929 implementation with domain resolution (`socks5h://` ), IPv4, IPv6, and username/password auth.
2026-09-07 08:02:09 +03:00
- **Dynamic space override**:
2026-09-07 07:45:50 +03:00
- Switch the target Gradio space on-the-fly per request using the `X-Gradio-Space` or `X-Space-URL` HTTP headers.
2026-09-07 08:02:09 +03:00
- **Fibonacci retry engine**:
2026-09-07 07:45:50 +03:00
- Resilient backoff retry mechanism (1s, 1s, 2s, 3s, 5s) for transient network hiccups.
2026-09-07 08:05:51 +03:00
## Installation
```bash
go install code.luxferre.top/luxferre/gr2gw@latest
```
2026-09-07 07:45:50 +03:00
## Build
```bash
make build
```
Binary will be compiled to `bin/gr2gw` .
To run tests:
```bash
make test
```
## Usage
2026-09-07 08:02:09 +03:00
### Quick start
2026-09-07 07:45:50 +03:00
Run with the default space (`https://ghost2513-openai-gpt-oss-120b.hf.space` ):
```bash
./bin/gr2gw -port 8080
```
Target any other Gradio space:
```bash
./bin/gr2gw -space https://ericsqin-hy3.hf.space -port 8080
```
With SOCKS5 proxy:
```bash
./bin/gr2gw -space https://ghost2513-openai-gpt-oss-120b.hf.space -socks socks5://127.0.0.1:1080
```
2026-09-07 08:02:09 +03:00
### CLI flags
2026-09-07 07:45:50 +03:00
| Flag | Default | Description |
|------|---------|-------------|
| `-space` , `-url` | `https://ghost2513-openai-gpt-oss-120b.hf.space` | Target Gradio space URL |
| `-port` | `8080` | Port to listen on |
| `-host` | `0.0.0.0` | Host interface to bind to |
| `-socks` , `-proxy` , `-socks5` | `""` | SOCKS5 proxy URL (`socks5://user:pass@host:port` ) |
| `-user-agent` , `-ua` | Firefox string | Custom User-Agent header |
| `-timeout` | `300` | Upstream request timeout in seconds |
2026-09-07 08:02:09 +03:00
### Environment variables
2026-09-07 07:45:50 +03:00
2026-09-07 08:02:09 +03:00
- `GRADIO_SPACE_URL` : default Gradio space URL fallback.
- `ALL_PROXY` , `SOCKS5_PROXY` , `SOCKS_PROXY` : default SOCKS5 proxy URL fallback.
2026-09-07 07:45:50 +03:00
2026-09-07 08:02:09 +03:00
## API examples
2026-09-07 07:45:50 +03:00
2026-09-07 08:02:09 +03:00
### List models
2026-09-07 07:45:50 +03:00
```bash
curl http://localhost:8080/v1/models
```
Response:
```json
{
"object" : "list" ,
"data" : [
{
"id" : "openai/gpt-oss-120b" ,
"object" : "model" ,
"created" : 1788756307 ,
"owned_by" : "gradio"
},
{
"id" : "gpt-oss-120b" ,
"object" : "model" ,
"created" : 1788756307 ,
"owned_by" : "gradio"
}
]
}
```
2026-09-07 08:02:09 +03:00
### Chat completions (non-streaming)
2026-09-07 07:45:50 +03:00
```bash
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-oss-120b",
"messages": [
{"role": "user", "content": "What is the capital of France?"}
]
}'
```
Response:
```json
{
"id" : "chatcmpl-16425f9d-c350-47a1-9a6d-e9ce10871545" ,
"object" : "chat.completion" ,
"created" : 1788756310 ,
"model" : "openai/gpt-oss-120b" ,
"choices" : [
{
"index" : 0 ,
"message" : {
"role" : "assistant" ,
"content" : "Paris is the capital of France."
},
"finish_reason" : "stop"
}
],
"usage" : {
"prompt_tokens" : 0 ,
"completion_tokens" : 0 ,
"total_tokens" : 0
}
}
```
2026-09-07 08:02:09 +03:00
### Chat completions (streaming)
2026-09-07 07:45:50 +03:00
```bash
curl -N http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-oss-120b",
"messages": [
{"role": "user", "content": "Count from 1 to 5."}
],
"stream": true
}'
```
2026-09-07 08:24:07 +03:00
### Tool calling (turn 1)
2026-09-07 07:45:50 +03:00
```bash
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-oss-120b",
"messages": [
{"role": "user", "content": "What is the weather in Tokyo?"}
],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather in a location",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"}
},
"required": ["location"]
}
}
}]
}'
```
Response:
```json
{
"id" : "chatcmpl-32727c62-ef2a-4866-855b-f1c7ec2b8023" ,
"object" : "chat.completion" ,
"created" : 1788756322 ,
"model" : "openai/gpt-oss-120b" ,
"choices" : [
{
"index" : 0 ,
"message" : {
"role" : "assistant" ,
"content" : null ,
"tool_calls" : [
{
"id" : "call_3d4c016a" ,
"type" : "function" ,
"function" : {
"name" : "get_weather" ,
"arguments" : "{\"location\":\"Tokyo\"}"
}
}
]
},
"finish_reason" : "tool_calls"
}
],
"usage" : {
"prompt_tokens" : 0 ,
"completion_tokens" : 0 ,
"total_tokens" : 0
}
}
```
2026-09-07 08:24:07 +03:00
### Tool response submission (turn 2)
```bash
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-oss-120b",
"messages": [
{"role": "user", "content": "What is the weather in Tokyo?"},
{
"role": "assistant",
"tool_calls": [
{
"id": "call_3d4c016a",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\":\"Tokyo\"}"
}
}
]
},
{
"role": "tool",
"tool_call_id": "call_3d4c016a",
"content": "{\"temperature\": 20, \"condition\": \"sunny\"}"
}
],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather in a location",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"}
},
"required": ["location"]
}
}
}]
}'
```
Response:
```json
{
"id" : "chatcmpl-4903ba12-f12b-4cd3-a801-7290bc91a421" ,
"object" : "chat.completion" ,
"created" : 1788756335 ,
"model" : "openai/gpt-oss-120b" ,
"choices" : [
{
"index" : 0 ,
"message" : {
"role" : "assistant" ,
"content" : "The current weather in Tokyo is sunny with a temperature of 20 °C."
},
"finish_reason" : "stop"
}
],
"usage" : {
"prompt_tokens" : 0 ,
"completion_tokens" : 0 ,
"total_tokens" : 0
}
}
```
2026-09-07 08:02:09 +03:00
### Dynamic target space override
2026-09-07 07:45:50 +03:00
Override the target space per request without restarting the server:
```bash
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-H "X-Gradio-Space: https://ericsqin-hy3.hf.space" \
-d '{
"messages": [
{"role": "user", "content": "Hello!"}
]
}'
```
2026-09-07 08:05:51 +03:00
## Credits
2026-09-07 07:45:50 +03:00
2026-09-07 08:05:51 +03:00
Created by Luxferre in 2026, released into the public domain with no warranties.