StarrySkyWorld commited on
Commit
0de3730
·
verified ·
1 Parent(s): 50ddbe9

Create main.go

Browse files
Files changed (1) hide show
  1. main.go +178 -0
main.go ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package main
2
+
3
+ import (
4
+ "bufio"
5
+ "bytes"
6
+ "encoding/json"
7
+ "fmt"
8
+ "io"
9
+ "log"
10
+ "net/http"
11
+ "os"
12
+ "strings"
13
+ "time"
14
+
15
+ "github.com/google/uuid"
16
+ )
17
+
18
+ const (
19
+ waveAPIURL = "https://cfapi.waveterm.dev/api/waveai"
20
+ )
21
+
22
+ type ResponsesRequest struct {
23
+ Model string `json:"model"`
24
+ Input json.RawMessage `json:"input"`
25
+ Instructions string `json:"instructions,omitempty"`
26
+ MaxOutputTokens int `json:"max_output_tokens,omitempty"`
27
+ Temperature float64 `json:"temperature,omitempty"`
28
+ TopP float64 `json:"top_p,omitempty"`
29
+ Stream *bool `json:"stream,omitempty"`
30
+ Tools json.RawMessage `json:"tools,omitempty"`
31
+ ToolChoice interface{} `json:"tool_choice,omitempty"`
32
+ Reasoning *ReasoningConfig `json:"reasoning,omitempty"`
33
+ Metadata map[string]interface{} `json:"metadata,omitempty"`
34
+ }
35
+
36
+ type ReasoningConfig struct {
37
+ Effort string `json:"effort,omitempty"`
38
+ Summary string `json:"summary,omitempty"`
39
+ }
40
+
41
+ type WaveRequest struct {
42
+ Input json.RawMessage `json:"input"`
43
+ Instructions string `json:"instructions,omitempty"`
44
+ MaxOutputTokens int `json:"max_output_tokens,omitempty"`
45
+ Model string `json:"model"`
46
+ Reasoning *ReasoningConfig `json:"reasoning,omitempty"`
47
+ Stream bool `json:"stream"`
48
+ StreamOptions *StreamOptions `json:"stream_options,omitempty"`
49
+ Text *TextConfig `json:"text,omitempty"`
50
+ Tools json.RawMessage `json:"tools,omitempty"`
51
+ ToolChoice interface{} `json:"tool_choice,omitempty"`
52
+ Temperature float64 `json:"temperature,omitempty"`
53
+ TopP float64 `json:"top_p,omitempty"`
54
+ }
55
+
56
+ type StreamOptions struct {
57
+ IncludeObfuscation bool `json:"include_obfuscation"`
58
+ }
59
+
60
+ type TextConfig struct {
61
+ Verbosity string `json:"verbosity,omitempty"`
62
+ }
63
+
64
+ func main() {
65
+ // Hugging Face 必须监听 7860
66
+ port := os.Getenv("PORT")
67
+ if port == "" {
68
+ port = "7860"
69
+ }
70
+
71
+ http.HandleFunc("/v1/responses", handleResponses)
72
+ http.HandleFunc("/responses", handleResponses)
73
+ http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
74
+ w.Write([]byte(`{"status":"ok"}`))
75
+ })
76
+
77
+ log.Printf("Server starting on :%s", port)
78
+ if err := http.ListenAndServe(":"+port, nil); err != nil {
79
+ log.Fatalf("Server failed: %v", err)
80
+ }
81
+ }
82
+
83
+ func handleResponses(w http.ResponseWriter, r *http.Request) {
84
+ reqID := uuid.New().String()[:8]
85
+ if r.Method != http.MethodPost {
86
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
87
+ return
88
+ }
89
+
90
+ body, _ := io.ReadAll(r.Body)
91
+ var req ResponsesRequest
92
+ json.Unmarshal(body, &req)
93
+
94
+ waveReq := convertToWaveRequest(&req)
95
+ stream := true
96
+ if req.Stream != nil {
97
+ stream = *req.Stream
98
+ }
99
+
100
+ if stream {
101
+ handleStreamingResponse(w, waveReq, reqID)
102
+ } else {
103
+ handleNonStreamingResponse(w, waveReq, reqID)
104
+ }
105
+ }
106
+
107
+ func convertToWaveRequest(req *ResponsesRequest) *WaveRequest {
108
+ maxTokens := req.MaxOutputTokens
109
+ if maxTokens == 0 {
110
+ maxTokens = 4096
111
+ }
112
+
113
+ return &WaveRequest{
114
+ Input: req.Input,
115
+ Instructions: req.Instructions,
116
+ MaxOutputTokens: maxTokens,
117
+ Model: "gpt-5.1",
118
+ Stream: true,
119
+ StreamOptions: &StreamOptions{IncludeObfuscation: false},
120
+ Text: &TextConfig{Verbosity: "low"},
121
+ Reasoning: &ReasoningConfig{Effort: "medium", Summary: "auto"},
122
+ }
123
+ }
124
+
125
+ func makeWaveRequest(waveReq *WaveRequest) (*http.Response, error) {
126
+ body, _ := json.Marshal(waveReq)
127
+ httpReq, _ := http.NewRequest(http.MethodPost, waveAPIURL, bytes.NewReader(body))
128
+
129
+ httpReq.Header.Set("Content-Type", "application/json")
130
+ httpReq.Header.Set("Accept", "text/event-stream")
131
+ httpReq.Header.Set("X-Wave-Apitype", "openai-responses")
132
+ httpReq.Header.Set("X-Wave-Clientid", uuid.New().String())
133
+
134
+ client := &http.Client{Timeout: 5 * time.Minute}
135
+ return client.Do(httpReq)
136
+ }
137
+
138
+ func handleStreamingResponse(w http.ResponseWriter, waveReq *WaveRequest, reqID string) {
139
+ resp, err := makeWaveRequest(waveReq)
140
+ if err != nil {
141
+ http.Error(w, err.Error(), http.StatusBadGateway)
142
+ return
143
+ }
144
+ defer resp.Body.Close()
145
+
146
+ w.Header().Set("Content-Type", "text/event-stream")
147
+ flusher, _ := w.(http.Flusher)
148
+ scanner := bufio.NewScanner(resp.Body)
149
+ for scanner.Scan() {
150
+ w.Write([]byte(scanner.Text() + "\n"))
151
+ flusher.Flush()
152
+ }
153
+ }
154
+
155
+ func handleNonStreamingResponse(w http.ResponseWriter, waveReq *WaveRequest, reqID string) {
156
+ resp, _ := makeWaveRequest(waveReq)
157
+ defer resp.Body.Close()
158
+
159
+ var outputText strings.Builder
160
+ scanner := bufio.NewScanner(resp.Body)
161
+ for scanner.Scan() {
162
+ line := scanner.Text()
163
+ if strings.HasPrefix(line, "data: ") {
164
+ var event map[string]interface{}
165
+ json.Unmarshal([]byte(strings.TrimPrefix(line, "data: ")), &event)
166
+ if delta, ok := event["delta"].(string); ok {
167
+ outputText.WriteString(delta)
168
+ }
169
+ }
170
+ }
171
+
172
+ w.Header().Set("Content-Type", "application/json")
173
+ json.NewEncoder(w).Encode(map[string]interface{}{
174
+ "choices": []map[string]interface{}{
175
+ {"message": map[string]string{"content": outputText.String()}},
176
+ },
177
+ })
178
+ }