one-api/types/chat.go
2024-05-21 03:10:23 +08:00

376 lines
12 KiB
Go

package types
const (
ContentTypeText = "text"
ContentTypeImageURL = "image_url"
)
const (
FinishReasonStop = "stop"
FinishReasonLength = "length"
FinishReasonFunctionCall = "function_call"
FinishReasonToolCalls = "tool_calls"
FinishReasonContentFilter = "content_filter"
FinishReasonNull = "null"
)
const (
ChatMessageRoleSystem = "system"
ChatMessageRoleUser = "user"
ChatMessageRoleAssistant = "assistant"
ChatMessageRoleFunction = "function"
ChatMessageRoleTool = "tool"
)
type ChatCompletionToolCallsFunction struct {
Name string `json:"name,omitempty"`
Arguments string `json:"arguments"`
}
type ChatCompletionToolCalls struct {
Id string `json:"id"`
Type string `json:"type"`
Function *ChatCompletionToolCallsFunction `json:"function"`
Index int `json:"index"`
}
type ChatCompletionMessage struct {
Role string `json:"role"`
Content any `json:"content,omitempty"`
Name *string `json:"name,omitempty"`
FunctionCall *ChatCompletionToolCallsFunction `json:"function_call,omitempty"`
ToolCalls []*ChatCompletionToolCalls `json:"tool_calls,omitempty"`
ToolCallID string `json:"tool_call_id,omitempty"`
}
func (m ChatCompletionMessage) StringContent() string {
content, ok := m.Content.(string)
if ok {
return content
}
contentList, ok := m.Content.([]any)
if ok {
var contentStr string
for _, contentItem := range contentList {
contentMap, ok := contentItem.(map[string]any)
if !ok {
continue
}
if subStr, ok := contentMap["text"].(string); ok && subStr != "" {
contentStr += subStr
}
}
return contentStr
}
return ""
}
func (m ChatCompletionMessage) ParseContent() []ChatMessagePart {
var contentList []ChatMessagePart
content, ok := m.Content.(string)
if ok {
contentList = append(contentList, ChatMessagePart{
Type: ContentTypeText,
Text: content,
})
return contentList
}
anyList, ok := m.Content.([]any)
if ok {
for _, contentItem := range anyList {
contentMap, ok := contentItem.(map[string]any)
if !ok {
continue
}
if subStr, ok := contentMap["text"].(string); ok && subStr != "" {
contentList = append(contentList, ChatMessagePart{
Type: ContentTypeText,
Text: subStr,
})
} else if subObj, ok := contentMap["image_url"].(map[string]any); ok {
contentList = append(contentList, ChatMessagePart{
Type: ContentTypeImageURL,
ImageURL: &ChatMessageImageURL{
URL: subObj["url"].(string),
},
})
} else if subObj, ok := contentMap["image"].(string); ok {
contentList = append(contentList, ChatMessagePart{
Type: ContentTypeImageURL,
ImageURL: &ChatMessageImageURL{
URL: subObj,
},
})
}
}
return contentList
}
return nil
}
// 将FunctionCall转换为ToolCalls
func (m *ChatCompletionMessage) FuncToToolCalls() {
if m.ToolCalls != nil {
return
}
if m.FunctionCall != nil {
m.ToolCalls = []*ChatCompletionToolCalls{
{
Type: ChatMessageRoleFunction,
Function: m.FunctionCall,
},
}
m.FunctionCall = nil
}
}
// 将ToolCalls转换为FunctionCall
func (m *ChatCompletionMessage) ToolToFuncCalls() {
if m.FunctionCall != nil {
return
}
if m.ToolCalls != nil {
m.FunctionCall = &ChatCompletionToolCallsFunction{
Name: m.ToolCalls[0].Function.Name,
Arguments: m.ToolCalls[0].Function.Arguments,
}
m.ToolCalls = nil
}
}
type ChatMessageImageURL struct {
URL string `json:"url,omitempty"`
Detail string `json:"detail,omitempty"`
}
type ChatMessagePart struct {
Type string `json:"type,omitempty"`
Text string `json:"text,omitempty"`
ImageURL *ChatMessageImageURL `json:"image_url,omitempty"`
}
type ChatCompletionResponseFormat struct {
Type string `json:"type,omitempty"`
}
type ChatCompletionRequest struct {
Model string `json:"model" binding:"required"`
Messages []ChatCompletionMessage `json:"messages" binding:"required"`
MaxTokens int `json:"max_tokens,omitempty"`
Temperature float64 `json:"temperature,omitempty"`
TopP float64 `json:"top_p,omitempty"`
N int `json:"n,omitempty"`
Stream bool `json:"stream,omitempty"`
Stop []string `json:"stop,omitempty"`
PresencePenalty float64 `json:"presence_penalty,omitempty"`
ResponseFormat *ChatCompletionResponseFormat `json:"response_format,omitempty"`
Seed *int `json:"seed,omitempty"`
FrequencyPenalty float64 `json:"frequency_penalty,omitempty"`
LogitBias any `json:"logit_bias,omitempty"`
LogProbs *bool `json:"logprobs,omitempty"`
TopLogProbs int `json:"top_logprobs,omitempty"`
User string `json:"user,omitempty"`
Functions []*ChatCompletionFunction `json:"functions,omitempty"`
FunctionCall any `json:"function_call,omitempty"`
Tools []*ChatCompletionTool `json:"tools,omitempty"`
ToolChoice any `json:"tool_choice,omitempty"`
}
func (r ChatCompletionRequest) GetFunctionCate() string {
if r.Tools != nil {
return "tool"
} else if r.Functions != nil {
return "function"
}
return ""
}
func (r *ChatCompletionRequest) GetFunctions() []*ChatCompletionFunction {
if r.Tools == nil && r.Functions == nil {
return nil
}
if r.Tools != nil {
var functions []*ChatCompletionFunction
for _, tool := range r.Tools {
functions = append(functions, &tool.Function)
}
return functions
}
return r.Functions
}
func (r *ChatCompletionRequest) ClearEmptyMessages() {
var messages []ChatCompletionMessage
for _, message := range r.Messages {
if message.StringContent() != "" || message.ToolCalls != nil || message.FunctionCall != nil {
messages = append(messages, message)
}
}
r.Messages = messages
}
type ChatCompletionFunction struct {
Name string `json:"name"`
Description string `json:"description"`
Parameters any `json:"parameters"`
}
type ChatCompletionTool struct {
Type string `json:"type"`
Function ChatCompletionFunction `json:"function"`
}
type ChatCompletionChoice struct {
Index int `json:"index"`
Message ChatCompletionMessage `json:"message"`
LogProbs any `json:"logprobs,omitempty"`
FinishReason any `json:"finish_reason,omitempty"`
ContentFilterResults any `json:"content_filter_results,omitempty"`
FinishDetails any `json:"finish_details,omitempty"`
}
func (c *ChatCompletionChoice) CheckChoice(request *ChatCompletionRequest) {
if request.Functions != nil && c.Message.ToolCalls != nil {
c.Message.ToolToFuncCalls()
c.FinishReason = FinishReasonFunctionCall
}
}
type ChatCompletionResponse struct {
ID string `json:"id"`
Object string `json:"object"`
Created int64 `json:"created"`
Model string `json:"model"`
Choices []ChatCompletionChoice `json:"choices"`
Usage *Usage `json:"usage,omitempty"`
SystemFingerprint string `json:"system_fingerprint,omitempty"`
PromptFilterResults any `json:"prompt_filter_results,omitempty"`
}
func (cc *ChatCompletionResponse) GetContent() string {
var content string
for _, choice := range cc.Choices {
content += choice.Message.StringContent()
}
return content
}
func (c ChatCompletionStreamChoice) ConvertOpenaiStream() []ChatCompletionStreamChoice {
var choices []ChatCompletionStreamChoice
var stopFinish string
if c.Delta.FunctionCall != nil {
stopFinish = FinishReasonFunctionCall
choices = c.Delta.FunctionCall.Split(&c, stopFinish, 0)
} else {
stopFinish = FinishReasonToolCalls
for index, tool := range c.Delta.ToolCalls {
choices = append(choices, tool.Function.Split(&c, stopFinish, index)...)
}
}
choices = append(choices, ChatCompletionStreamChoice{
Index: c.Index,
Delta: ChatCompletionStreamChoiceDelta{},
FinishReason: stopFinish,
})
return choices
}
func (f *ChatCompletionToolCallsFunction) Split(c *ChatCompletionStreamChoice, stopFinish string, index int) []ChatCompletionStreamChoice {
var functions []*ChatCompletionToolCallsFunction
var choices []ChatCompletionStreamChoice
functions = append(functions, &ChatCompletionToolCallsFunction{
Name: f.Name,
Arguments: "",
})
if f.Arguments == "" || f.Arguments == "{}" {
functions = append(functions, &ChatCompletionToolCallsFunction{
Arguments: "{}",
})
} else {
functions = append(functions, &ChatCompletionToolCallsFunction{
Arguments: f.Arguments,
})
}
for fIndex, function := range functions {
choice := ChatCompletionStreamChoice{
Index: c.Index,
Delta: ChatCompletionStreamChoiceDelta{
Role: c.Delta.Role,
},
}
if stopFinish == FinishReasonFunctionCall {
choice.Delta.FunctionCall = function
} else {
toolCalls := &ChatCompletionToolCalls{
// Id: c.Delta.ToolCalls[0].Id,
Index: index,
Type: ChatMessageRoleFunction,
Function: function,
}
if fIndex == 0 {
toolCalls.Id = c.Delta.ToolCalls[0].Id
}
choice.Delta.ToolCalls = []*ChatCompletionToolCalls{toolCalls}
}
choices = append(choices, choice)
}
return choices
}
type ChatCompletionStreamChoiceDelta struct {
Content string `json:"content,omitempty"`
Role string `json:"role,omitempty"`
FunctionCall *ChatCompletionToolCallsFunction `json:"function_call,omitempty"`
ToolCalls []*ChatCompletionToolCalls `json:"tool_calls,omitempty"`
}
func (m *ChatCompletionStreamChoiceDelta) ToolToFuncCalls() {
if m.FunctionCall != nil {
return
}
if m.ToolCalls != nil {
m.FunctionCall = &ChatCompletionToolCallsFunction{
Name: m.ToolCalls[0].Function.Name,
Arguments: m.ToolCalls[0].Function.Arguments,
}
m.ToolCalls = nil
}
}
type ChatCompletionStreamChoice struct {
Index int `json:"index"`
Delta ChatCompletionStreamChoiceDelta `json:"delta"`
FinishReason any `json:"finish_reason"`
ContentFilterResults any `json:"content_filter_results,omitempty"`
}
func (c *ChatCompletionStreamChoice) CheckChoice(request *ChatCompletionRequest) {
if request.Functions != nil && c.Delta.ToolCalls != nil {
c.Delta.ToolToFuncCalls()
c.FinishReason = FinishReasonToolCalls
}
}
type ChatCompletionStreamResponse struct {
ID string `json:"id"`
Object string `json:"object"`
Created int64 `json:"created"`
Model string `json:"model"`
Choices []ChatCompletionStreamChoice `json:"choices"`
PromptAnnotations any `json:"prompt_annotations,omitempty"`
}