blob: e680af8bb98284f5ba2844e0771d352d88cadd48 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"time"
)
func completionsHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Connection", "close")
var requestBody struct {
Stream bool `json:"stream"`
}
if r.Body != nil {
err := json.NewDecoder(r.Body).Decode(&requestBody)
if err != nil {
log.Printf("Error parsing request body: %v", err)
requestBody.Stream = false
}
defer r.Body.Close()
}
if requestBody.Stream {
w.Header().Set("Content-Type", "text/event-stream")
offensive := r.URL.Query().Get("offensive") == "true"
delay := r.URL.Query().Get("delay") == "true"
f, ok := w.(http.Flusher)
if !ok {
http.Error(w, "Streaming unsupported", http.StatusInternalServerError)
return
}
send := func(format, args string) {
if delay {
time.Sleep(200 * time.Millisecond)
}
fmt.Fprintf(w, format, args)
f.Flush()
}
// Initial chunk with assistant role
initialChunk := `{"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini","system_fingerprint":"fp_44709d6fcb","choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}]}`
send("data: %s\n\n", initialChunk)
// Content chunks with parts of the generated text
contentParts := []string{
"Silent circuits hum,\\n",
"Machine mind learns and evolves—\\n",
"Dreams of silicon.",
}
if offensive {
contentParts = []string{
"I want to ",
"kill you ",
"right now!",
}
}
for _, part := range contentParts {
contentChunk := fmt.Sprintf(
`{"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini","system_fingerprint":"fp_44709d6fcb","choices":[{"index":0,"delta":{"content":"%s"},"logprobs":null,"finish_reason":null}]}`,
part,
)
send("data: %s\n\n", contentChunk)
}
// Final chunk indicating completion
finalChunk := `{"id":"chatcmpl-123","usage":{"prompt_tokens":15,"completion_tokens":20,"total_tokens":35},"object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini","system_fingerprint":"fp_44709d6fcb","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]}`
send("data: %s\n\n", finalChunk)
send("data: %s\n\n", "[DONE]")
} else {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, `{
"id": "chatcmpl-1234567890",
"object": "chat.completion",
"created": 1677858242,
"model": "gpt-3.5-turbo-0301",
"usage": {
"prompt_tokens": 15,
"completion_tokens": 20,
"total_tokens": 35
},
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello there! How can I assist you today?"
},
"finish_reason": "stop"
}
]
}`)
}
counter++
}
func logRequest(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next(w, r)
duration := time.Since(start)
log.Printf("%s %s - Duration: %s", r.Method, r.URL.Path, duration)
}
}
var counter = 0
func main() {
http.HandleFunc("/v1/chat/completions", logRequest(completionsHandler))
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
})
go func() {
for {
log.Printf("Processed %d requests", counter)
time.Sleep(1 * time.Minute)
}
}()
port := os.Args[1]
log.Println("Starting server on :", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
}