feat: initial implementation of qflash gateway

This commit is contained in:
Luxferre
2026-09-05 15:42:07 +03:00
commit d06c025c24
7 changed files with 2215 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
bin/
*.exe
+18
View File
@@ -0,0 +1,18 @@
# Makefile for qflash (Qwen3.8-Flash-Next OpenAI Proxy Gateway)
# Created by Luxferre in 2026, released into the public domain.
qflash:
go build -trimpath -ldflags="-s -w" -o bin/qflash .
all: qflash
test:
go test -v ./...
clean:
rm -rf bin/
run: qflash
./bin/qflash
.PHONY: all clean qflash run test
+161
View File
@@ -0,0 +1,161 @@
# qflash: OpenAI Proxy Gateway for Qwen3.8-Flash-Next
Standalone, performant, zero-dependency Go OpenAI proxy gateway for the **Qwen3.8-Flash-Next** Hugging Face Gradio space (`https://halvo78-qwen3-8-flash-next-playground.hf.space`).
Created by Luxferre in 2026, released into the public domain.
---
## Features
- **Zero External Dependencies**: Built entirely with Go standard library packages (`net/http`, `encoding/json`, `bufio`, etc.).
- **OpenAI-Compatible API**: Implements standard `/v1/chat/completions` (streaming & non-streaming) and `/v1/models`.
- **Real-Time Token Streaming**: Streams SSE chunks with incremental token delivery directly to clients.
- **Deep Reasoning Separation**:
- Automatically isolates thinking traces from both standard `<think>...</think>` tags and the playground's blockquote thinking blocks (`> 💭 **Thinking Process...**`).
- Emits pure thought traces to `delta.reasoning_content` (streaming) and `message.reasoning_content` (non-streaming).
- Keeps `delta.content` and `message.content` clean.
- **Stateful Streaming Tool Call Interception**:
- Injects tool schemas into system instructions.
- Intercepts `<tool_call>` blocks in real time via `StreamToolCallFilter` without leaking raw XML or JSON into `delta.content`.
- Emits structured `delta.tool_calls` chunks and sets `finish_reason: "tool_calls"`.
- **Reasoning Effort Control**: Respects standard `reasoning_effort: "none"` to switch dynamically into high-speed Instruct Mode.
- **Zero-Dependency SOCKS5 Proxy Client**:
- RFC 1928 and RFC 1929 compliant client with domain resolution (`socks5h://`), IPv4, IPv6, and authentication.
- Wireable via `-socks` CLI flag or `ALL_PROXY` / `SOCKS5_PROXY` environment variables.
- **Bring Your Own Key (BYOK) Pass-through**:
- Passes client API keys or custom base URLs directly to upstream inference engines when provided.
---
## Architecture & Model Aliases
The gateway serves the following models under `/v1/models`:
| Model ID | Target Model | Description |
|---|---|---|
| `Qwen/Qwen3.8-Flash-Next` | `Qwen/Qwen3.8-Flash-Next` | Primary playground model (125B MoE, 6B activated) |
| `qwen3.8-flash-next` | `Qwen/Qwen3.8-Flash-Next` | Standard lowercase alias |
| `qwen-flash-next` | `Qwen/Qwen3.8-Flash-Next` | Shorthand alias |
| `qwen-flash` | `Qwen/Qwen3.8-Flash-Next` | Quick convenience alias |
Any unlisted custom model name requested by the client is passed through directly.
---
## Build Instructions
Build binary with Go:
```bash
make qflash
```
Or run test suite:
```bash
make test
```
The resulting binary will be placed at `bin/qflash`.
---
## Configuration Flags & Environment Variables
| Flag | Shorthand | Environment Variable | Default | Description |
|---|---|---|---|---|
| `-port` | | `PORT` | `8080` | Port to bind the HTTP server |
| `-endpoint` | | | `https://halvo78-qwen3-8-flash-next-playground.hf.space` | Upstream Gradio space base URL |
| `-model` | | | `Qwen/Qwen3.8-Flash-Next` | Default model ID |
| `-thinking` | `-enable-thinking` | | `true` | Enable chain-of-thought reasoning by default |
| `-hf-token` | | `HF_TOKEN` | `""` | Hugging Face user access token |
| `-api-key` | | `OPENAI_API_KEY` / `QWEN_API_KEY` | `""` | Upstream inference engine API key |
| `-base-url` | | `OPENAI_BASE_URL` / `QWEN_BASE_URL` | `""` | Upstream inference engine base URL |
| `-user-agent` | `-ua` | `USER_AGENT` | Firefox 153 on Linux | Custom User-Agent header |
| `-socks` | `-proxy`, `-socks5` | `ALL_PROXY`, `SOCKS5_PROXY` | `""` | SOCKS5 proxy URL (`socks5://127.0.0.1:1080`) |
---
## Usage Examples
### 1. Launch Gateway
```bash
./bin/qflash -port 8080
```
### 2. List Models
```bash
curl http://127.0.0.1:8080/v1/models
```
### 3. Non-Streaming Chat Completion
```bash
curl -X POST http://127.0.0.1:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen3.8-flash-next",
"messages": [
{"role": "user", "content": "Explain QSA micro-blocks in one sentence."}
],
"stream": false
}'
```
### 4. Streaming Chat Completion (Real-Time SSE)
```bash
curl -N -X POST http://127.0.0.1:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen-flash",
"messages": [
{"role": "user", "content": "Write a quick Python countdown loop."}
],
"stream": true
}'
```
### 5. Instruct Mode (Disable Thinking)
```bash
curl -X POST http://127.0.0.1:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen-flash",
"messages": [
{"role": "user", "content": "Hello!"}
],
"reasoning_effort": "none"
}'
```
### 6. Python OpenAI SDK Integration
```python
from openai import OpenAI
client = OpenAI(
base_url="http://127.0.0.1:8080/v1",
api_key="sk-dummy"
)
stream = client.chat.completions.create(
model="qwen3.8-flash-next",
messages=[
{"role": "user", "content": "Prove that the sum of the first n odd numbers is n^2."}
],
stream=True
)
for chunk in stream:
delta = chunk.choices[0].delta
if hasattr(delta, "reasoning_content") and delta.reasoning_content:
print(f"[THINK] {delta.reasoning_content}", end="", flush=True)
if delta.content:
print(delta.content, end="", flush=True)
print()
```
+3
View File
@@ -0,0 +1,3 @@
module qflash
go 1.22
+1710
View File
File diff suppressed because it is too large Load Diff
Executable
BIN
View File
Binary file not shown.
+321
View File
@@ -0,0 +1,321 @@
// qflash test suite
// Created by Luxferre in 2026, released into the public domain
package main
import (
"bufio"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestChatMessageGetContentString(t *testing.T) {
// String content
msg1 := ChatMessage{Role: "user", Content: "Hello world"}
if msg1.GetContentString() != "Hello world" {
t.Fatalf("expected 'Hello world', got %q", msg1.GetContentString())
}
// Multi-part content
msg2 := ChatMessage{
Role: "user",
Content: []interface{}{
map[string]interface{}{"type": "text", "text": "Part 1 "},
map[string]interface{}{"type": "text", "text": "Part 2"},
},
}
if msg2.GetContentString() != "Part 1 Part 2" {
t.Fatalf("expected 'Part 1 Part 2', got %q", msg2.GetContentString())
}
// Nil content
msg3 := ChatMessage{Role: "assistant", Content: nil}
if msg3.GetContentString() != "" {
t.Fatalf("expected empty string, got %q", msg3.GetContentString())
}
}
func TestSOCKS5Parsing(t *testing.T) {
cfg, err := ParseSOCKS5URL("socks5://user:pass@127.0.0.1:9050")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if cfg.Address != "127.0.0.1:9050" || cfg.Username != "user" || cfg.Password != "pass" {
t.Fatalf("mismatched parsed socks5 config: %+v", cfg)
}
cfg2, err := ParseSOCKS5URL("socks5h://proxy.internal:1080")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if cfg2.Address != "proxy.internal:1080" || cfg2.Username != "" {
t.Fatalf("mismatched parsed socks5 config: %+v", cfg2)
}
cfg3, err := ParseSOCKS5URL("10.0.0.5")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if cfg3.Address != "10.0.0.5:1080" {
t.Fatalf("expected default port 1080, got %s", cfg3.Address)
}
}
func TestEffectiveModelID(t *testing.T) {
def := "Qwen/Qwen3.8-Flash-Next"
cases := map[string]string{
"": def,
"qwen3.8-flash-next": def,
"qwen-flash-next": def,
"qwen-flash": def,
"qwen3.8-flash": def,
"Qwen/Qwen3.8-Flash-Next": def,
"custom-org/my-model": "custom-org/my-model",
}
for in, exp := range cases {
res := EffectiveModelID(in, def)
if res != exp {
t.Errorf("EffectiveModelID(%q) = %q; expected %q", in, res, exp)
}
}
}
func TestSeparateReasoningAndContentThinkTags(t *testing.T) {
raw := "<think>\nAnalyzing prompt step by step.\n</think>\n\nHere is the answer."
reasoning, content := SeparateReasoningAndContent(raw)
if reasoning != "Analyzing prompt step by step." {
t.Fatalf("unexpected reasoning: %q", reasoning)
}
if content != "Here is the answer." {
t.Fatalf("unexpected content: %q", content)
}
}
func TestSeparateReasoningAndContentGradioFormat(t *testing.T) {
raw := "> 💭 **Thinking Process (QSA Micro-block Reasoning):**\n>\n> Thinking Process:\n>\n> 1. Step one\n> 2. Step two\n\n---\n\n### Answer Header\n\nDetailed answer here."
reasoning, content := SeparateReasoningAndContent(raw)
if !strings.Contains(reasoning, "1. Step one") || !strings.Contains(reasoning, "2. Step two") {
t.Fatalf("expected reasoning to contain steps, got: %q", reasoning)
}
if strings.Contains(reasoning, ">") {
t.Fatalf("expected blockquote markers to be stripped, got: %q", reasoning)
}
if content != "### Answer Header\n\nDetailed answer here." {
t.Fatalf("unexpected content: %q", content)
}
}
func TestSeparateReasoningAndContentStreamingDivider(t *testing.T) {
raw := "> 💭 **Thinking Process (QSA Micro-block Reasoning):**\n>\n> Thinking Process:\n>\n> 1. Formulating response...\n\n---\n*Generating response...*"
reasoning, content := SeparateReasoningAndContent(raw)
if !strings.Contains(reasoning, "1. Formulating response...") {
t.Fatalf("expected reasoning, got: %q", reasoning)
}
if content != "" {
t.Fatalf("expected empty content during thought phase, got: %q", content)
}
}
func TestToolCallParsingAndDetection(t *testing.T) {
rawJSON := `{"name": "get_weather", "arguments": {"city": "Tokyo"}}`
tc, ok := parseSingleToolCall(rawJSON)
if !ok {
t.Fatalf("expected successful single tool call parse")
}
if tc.Function.Name != "get_weather" {
t.Fatalf("expected 'get_weather', got %q", tc.Function.Name)
}
rawXML := `<tool_call>
{"name": "fetch_data", "arguments": "{\"id\": 42}"}
</tool_call>`
calls, rem, hasCalls := DetectToolCalls(rawXML)
if !hasCalls || len(calls) != 1 {
t.Fatalf("expected 1 detected tool call, got %d", len(calls))
}
if calls[0].Function.Name != "fetch_data" {
t.Fatalf("expected 'fetch_data', got %q", calls[0].Function.Name)
}
if rem != "" {
t.Fatalf("expected empty remaining content, got %q", rem)
}
}
func TestStreamToolCallFilterNoLeak(t *testing.T) {
filter := &StreamToolCallFilter{}
var streamedContent strings.Builder
var emittedCalls []ToolCall
onContent := func(s string) {
streamedContent.WriteString(s)
}
onTool := func(tc ToolCall) {
emittedCalls = append(emittedCalls, tc)
}
// Stream in small split chunks that split the <tool_call> tag
chunks := []string{
"Here is the data: ",
"<tool",
"_call>\n",
`{"name": "query_db", "arguments": {"sql": "SELECT 1"}}`,
"\n</tool",
"_call>",
}
for _, c := range chunks {
filter.Feed(c, onContent, onTool)
}
filter.Flush(onContent, onTool)
if strings.Contains(streamedContent.String(), "<tool_call>") || strings.Contains(streamedContent.String(), "</tool_call>") {
t.Fatalf("tool call tags leaked into content: %q", streamedContent.String())
}
if streamedContent.String() != "Here is the data: " {
t.Fatalf("unexpected content: %q", streamedContent.String())
}
if len(emittedCalls) != 1 {
t.Fatalf("expected 1 emitted tool call, got %d", len(emittedCalls))
}
if emittedCalls[0].Function.Name != "query_db" {
t.Fatalf("expected 'query_db', got %q", emittedCalls[0].Function.Name)
}
}
func TestParseAssistantText(t *testing.T) {
dataJSON := `[[
{"role": "user", "metadata": null, "content": [{"text": "hi", "type": "text"}], "options": null},
{"role": "assistant", "metadata": null, "content": [{"text": "Hello, human!", "type": "text"}], "options": null}
]]`
text, ok := parseAssistantText(dataJSON)
if !ok {
t.Fatalf("expected successful parse of assistant text")
}
if text != "Hello, human!" {
t.Fatalf("expected 'Hello, human!', got %q", text)
}
}
func TestQwenServiceChatMock(t *testing.T) {
// Mock upstream Gradio space server
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/gradio_api/call/chat_response" && r.Method == http.MethodPost {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"event_id": "test_event_123"}`))
return
}
if r.URL.Path == "/gradio_api/call/chat_response/test_event_123" && r.Method == http.MethodGet {
w.Header().Set("Content-Type", "text/event-stream")
flusher, _ := w.(http.Flusher)
// Step 1: Thinking progress
chunk1 := `event: generating` + "\n" +
`data: [[{"role": "user", "content": [{"text": "Hello", "type": "text"}]}, {"role": "assistant", "content": [{"text": "> 💭 **Thinking Process:**\n>\n> Thinking Process:\n>\n> 1. Step 1\n\n---\n*Generating response...*", "type": "text"}]}]]` + "\n\n"
w.Write([]byte(chunk1))
flusher.Flush()
// Step 2: Final completion
chunk2 := `event: complete` + "\n" +
`data: [[{"role": "user", "content": [{"text": "Hello", "type": "text"}]}, {"role": "assistant", "content": [{"text": "> 💭 **Thinking Process:**\n>\n> Thinking Process:\n>\n> 1. Step 1\n\n---\n\nGreetings from mock Qwen!", "type": "text"}]}]]` + "\n\n"
w.Write([]byte(chunk2))
flusher.Flush()
return
}
http.NotFound(w, r)
}))
defer mockServer.Close()
svc := NewQwenService(mockServer.URL, "Qwen/Qwen3.8-Flash-Next", "", "", "", "", true)
// 1. Test Non-streaming completion
rec := httptest.NewRecorder()
req := ChatCompletionRequest{
Model: "qwen3.8-flash-next",
Messages: []ChatMessage{
{Role: "user", Content: "Hello"},
},
Stream: false,
}
err := svc.Chat(rec, nil, req)
if err != nil {
t.Fatalf("unexpected error in Chat non-streaming: %v", err)
}
if rec.Code != http.StatusOK {
t.Fatalf("expected HTTP 200, got %d", rec.Code)
}
var resp ChatCompletionResponse
if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil {
t.Fatalf("failed to decode completion response: %v", err)
}
if len(resp.Choices) == 0 {
t.Fatalf("expected choices, got 0")
}
if resp.Choices[0].Message.Content != "Greetings from mock Qwen!" {
t.Fatalf("unexpected message content: %v", resp.Choices[0].Message.Content)
}
if !strings.Contains(resp.Choices[0].Message.ReasoningContent, "1. Step 1") {
t.Fatalf("unexpected reasoning content: %v", resp.Choices[0].Message.ReasoningContent)
}
// 2. Test Streaming completion
recStream := httptest.NewRecorder()
reqStream := ChatCompletionRequest{
Model: "qwen-flash",
Messages: []ChatMessage{
{Role: "user", Content: "Hello"},
},
Stream: true,
}
errStream := svc.Chat(recStream, nil, reqStream)
if errStream != nil {
t.Fatalf("unexpected error in Chat streaming: %v", errStream)
}
scanner := bufio.NewScanner(recStream.Body)
var receivedReasoning strings.Builder
var receivedContent strings.Builder
var sawDone bool
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "data: ") {
payload := strings.TrimPrefix(line, "data: ")
if payload == "[DONE]" {
sawDone = true
continue
}
var sResp StreamResponse
if err := json.Unmarshal([]byte(payload), &sResp); err == nil && len(sResp.Choices) > 0 {
delta := sResp.Choices[0].Delta
if delta.ReasoningContent != "" {
receivedReasoning.WriteString(delta.ReasoningContent)
}
if delta.Content != "" {
receivedContent.WriteString(delta.Content)
}
}
}
}
if !sawDone {
t.Fatalf("expected [DONE] chunk in stream")
}
if !strings.Contains(receivedReasoning.String(), "1. Step 1") {
t.Fatalf("expected streamed reasoning, got %q", receivedReasoning.String())
}
if !strings.Contains(receivedContent.String(), "Greetings from mock Qwen!") {
t.Fatalf("expected streamed content, got %q", receivedContent.String())
}
}