in openai/chat.go [30:97]
func (h *handlers) ChatCompletionsHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
internal.ErrorHandler(w, r, http.StatusMethodNotAllowed, "method not allowed")
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
internal.ErrorHandler(w, r, http.StatusInternalServerError, "failed to read request body: %v", err)
return
}
defer r.Body.Close()
var chatReq ChatCompletionRequest
if err := json.Unmarshal(body, &chatReq); err != nil {
internal.ErrorHandler(w, r, http.StatusInternalServerError, "failed to parse chat completions body: %v", err)
return
}
model := h.geminiClient.GenerativeModel(chatReq.Model)
model.GenerationConfig = genai.GenerationConfig{
CandidateCount: chatReq.N,
StopSequences: chatReq.Stop,
ResponseMIMEType: "text/plain",
MaxOutputTokens: chatReq.MaxTokens,
Temperature: chatReq.Temperature,
TopP: chatReq.TopP,
}
chat := model.StartChat()
var lastPart genai.Part
for i, r := range chatReq.Messages {
if r.Role == "system" {
model.SystemInstruction = &genai.Content{
Role: r.Role,
Parts: []genai.Part{genai.Text(r.Content)},
}
continue
}
if i == len(chatReq.Messages)-1 { // the last message
// TODO(jbd): This hack strips away the role of the last message.
// But Gemini API Go SDK doesn't give flexibility to call SendMessage
// with a list of contents.
lastPart = genai.Text(r.Content)
break
}
chat.History = append(chat.History, &genai.Content{
Role: r.Role,
Parts: []genai.Part{genai.Text(r.Content)},
})
}
if chatReq.Stream {
streamingChatCompletionsHandler(w, r, chatReq.Model, chat, lastPart)
return
}
geminiResp, err := chat.SendMessage(r.Context(), lastPart)
if err != nil {
internal.ErrorHandler(w, r, http.StatusInternalServerError, "failed to generate content: %v", err)
return
}
resp := toOpenAIResponse(geminiResp, "chat.completion", chatReq.Model)
if err := json.NewEncoder(w).Encode(resp); err != nil {
internal.ErrorHandler(w, r, http.StatusInternalServerError, "failed to encode chat completions response: %v", err)
return
}
}