| package main | |
| import ( | |
| "bufio" | |
| "bytes" | |
| "encoding/json" | |
| "io" | |
| "log" | |
| "net/http" | |
| "os" | |
| "strings" | |
| "github.com/google/uuid" | |
| ) | |
| const ( | |
| waveAPIURL = "https://cfapi.waveterm.dev/api/waveai" | |
| ) | |
| type ResponsesRequest struct { | |
| Model string `json:"model"` | |
| Input json.RawMessage `json:"input"` | |
| Instructions string `json:"instructions,omitempty"` | |
| MaxOutputTokens int `json:"max_output_tokens,omitempty"` | |
| Temperature float64 `json:"temperature,omitempty"` | |
| TopP float64 `json:"top_p,omitempty"` | |
| Stream *bool `json:"stream,omitempty"` | |
| Tools json.RawMessage `json:"tools,omitempty"` | |
| ToolChoice interface{} `json:"tool_choice,omitempty"` | |
| Reasoning *ReasoningConfig `json:"reasoning,omitempty"` | |
| Metadata map[string]interface{} `json:"metadata,omitempty"` | |
| } | |
| type ReasoningConfig struct { | |
| Effort string `json:"effort,omitempty"` | |
| Summary string `json:"summary,omitempty"` | |
| } | |
| type WaveRequest struct { | |
| Input json.RawMessage `json:"input"` | |
| Instructions string `json:"instructions,omitempty"` | |
| MaxOutputTokens int `json:"max_output_tokens,omitempty"` | |
| Model string `json:"model"` | |
| Reasoning *ReasoningConfig `json:"reasoning,omitempty"` | |
| Stream bool `json:"stream"` | |
| StreamOptions *StreamOptions `json:"stream_options,omitempty"` | |
| Text *TextConfig `json:"text,omitempty"` | |
| Tools json.RawMessage `json:"tools,omitempty"` | |
| ToolChoice interface{} `json:"tool_choice,omitempty"` | |
| Temperature float64 `json:"temperature,omitempty"` | |
| TopP float64 `json:"top_p,omitempty"` | |
| } | |
| type StreamOptions struct { | |
| IncludeObfuscation bool `json:"include_obfuscation"` | |
| } | |
| type TextConfig struct { | |
| Verbosity string `json:"verbosity,omitempty"` | |
| } | |
| func main() { | |
| port := os.Getenv("PORT") | |
| if port == "" { | |
| port = "7860" | |
| } | |
| http.HandleFunc("/v1/responses", handleResponses) | |
| http.HandleFunc("/responses", handleResponses) | |
| http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { | |
| w.Write([]byte(`{"status":"ok"}`)) | |
| }) | |
| log.Printf("Server starting on :%s", port) | |
| if err := http.ListenAndServe(":"+port, nil); err != nil { | |
| log.Fatalf("Server failed: %v", err) | |
| } | |
| } | |
| func handleResponses(w http.ResponseWriter, r *http.Request) { | |
| if r.Method != http.MethodPost { | |
| http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | |
| return | |
| } | |
| body, _ := io.ReadAll(r.Body) | |
| var req ResponsesRequest | |
| json.Unmarshal(body, &req) | |
| waveReq := convertToWaveRequest(&req) | |
| stream := true | |
| if req.Stream != nil { | |
| stream = *req.Stream | |
| } | |
| if stream { | |
| handleStreamingResponse(w, waveReq) | |
| } else { | |
| handleNonStreamingResponse(w, waveReq) | |
| } | |
| } | |
| func convertToWaveRequest(req *ResponsesRequest) *WaveRequest { | |
| maxTokens := req.MaxOutputTokens | |
| if maxTokens == 0 { | |
| maxTokens = 4096 | |
| } | |
| return &WaveRequest{ | |
| Input: req.Input, | |
| Instructions: req.Instructions, | |
| MaxOutputTokens: maxTokens, | |
| Model: "gpt-5.1", | |
| Stream: true, | |
| StreamOptions: &StreamOptions{IncludeObfuscation: false}, | |
| Text: &TextConfig{Verbosity: "low"}, | |
| Reasoning: &ReasoningConfig{Effort: "medium", Summary: "auto"}, | |
| } | |
| } | |
| func makeWaveRequest(waveReq *WaveRequest) (*http.Response, error) { | |
| body, _ := json.Marshal(waveReq) | |
| httpReq, _ := http.NewRequest(http.MethodPost, waveAPIURL, bytes.NewReader(body)) | |
| httpReq.Header.Set("Content-Type", "application/json") | |
| httpReq.Header.Set("Accept", "text/event-stream") | |
| httpReq.Header.Set("X-Wave-Apitype", "openai-responses") | |
| httpReq.Header.Set("X-Wave-Clientid", uuid.New().String()) | |
| client := &http.Client{} | |
| return client.Do(httpReq) | |
| } | |
| func handleStreamingResponse(w http.ResponseWriter, waveReq *WaveRequest) { | |
| resp, err := makeWaveRequest(waveReq) | |
| if err != nil { | |
| http.Error(w, err.Error(), http.StatusBadGateway) | |
| return | |
| } | |
| defer resp.Body.Close() | |
| w.Header().Set("Content-Type", "text/event-stream") | |
| flusher, _ := w.(http.Flusher) | |
| scanner := bufio.NewScanner(resp.Body) | |
| for scanner.Scan() { | |
| w.Write([]byte(scanner.Text() + "\n")) | |
| flusher.Flush() | |
| } | |
| } | |
| func handleNonStreamingResponse(w http.ResponseWriter, waveReq *WaveRequest) { | |
| resp, _ := makeWaveRequest(waveReq) | |
| defer resp.Body.Close() | |
| var outputText strings.Builder | |
| scanner := bufio.NewScanner(resp.Body) | |
| for scanner.Scan() { | |
| line := scanner.Text() | |
| if strings.HasPrefix(line, "data: ") { | |
| var event map[string]interface{} | |
| json.Unmarshal([]byte(strings.TrimPrefix(line, "data: ")), &event) | |
| if delta, ok := event["delta"].(string); ok { | |
| outputText.WriteString(delta) | |
| } | |
| } | |
| } | |
| w.Header().Set("Content-Type", "application/json") | |
| json.NewEncoder(w).Encode(map[string]interface{}{ | |
| "choices": []map[string]interface{}{ | |
| {"message": map[string]string{"content": outputText.String()}}, | |
| }, | |
| }) | |
| } |