| package dto
|
|
|
| import (
|
| "encoding/json"
|
| "fmt"
|
| "strings"
|
|
|
| "github.com/QuantumNous/new-api/common"
|
| "github.com/QuantumNous/new-api/types"
|
|
|
| "github.com/gin-gonic/gin"
|
| )
|
|
|
| type ResponseFormat struct {
|
| Type string `json:"type,omitempty"`
|
| JsonSchema json.RawMessage `json:"json_schema,omitempty"`
|
| }
|
|
|
| type FormatJsonSchema struct {
|
| Description string `json:"description,omitempty"`
|
| Name string `json:"name"`
|
| Schema any `json:"schema,omitempty"`
|
| Strict json.RawMessage `json:"strict,omitempty"`
|
| }
|
|
|
|
|
|
|
| type GeneralOpenAIRequest struct {
|
| Model string `json:"model,omitempty"`
|
| Messages []Message `json:"messages,omitempty"`
|
| Prompt any `json:"prompt,omitempty"`
|
| Prefix any `json:"prefix,omitempty"`
|
| Suffix any `json:"suffix,omitempty"`
|
| Stream bool `json:"stream,omitempty"`
|
| StreamOptions *StreamOptions `json:"stream_options,omitempty"`
|
| MaxTokens uint `json:"max_tokens,omitempty"`
|
| MaxCompletionTokens uint `json:"max_completion_tokens,omitempty"`
|
| ReasoningEffort string `json:"reasoning_effort,omitempty"`
|
| Verbosity json.RawMessage `json:"verbosity,omitempty"`
|
| Temperature *float64 `json:"temperature,omitempty"`
|
| TopP float64 `json:"top_p,omitempty"`
|
| TopK int `json:"top_k,omitempty"`
|
| Stop any `json:"stop,omitempty"`
|
| N int `json:"n,omitempty"`
|
| Input any `json:"input,omitempty"`
|
| Instruction string `json:"instruction,omitempty"`
|
| Size string `json:"size,omitempty"`
|
| Functions json.RawMessage `json:"functions,omitempty"`
|
| FrequencyPenalty float64 `json:"frequency_penalty,omitempty"`
|
| PresencePenalty float64 `json:"presence_penalty,omitempty"`
|
| ResponseFormat *ResponseFormat `json:"response_format,omitempty"`
|
| EncodingFormat json.RawMessage `json:"encoding_format,omitempty"`
|
| Seed float64 `json:"seed,omitempty"`
|
| ParallelTooCalls *bool `json:"parallel_tool_calls,omitempty"`
|
| Tools []ToolCallRequest `json:"tools,omitempty"`
|
| ToolChoice any `json:"tool_choice,omitempty"`
|
| User string `json:"user,omitempty"`
|
| LogProbs bool `json:"logprobs,omitempty"`
|
| TopLogProbs int `json:"top_logprobs,omitempty"`
|
| Dimensions int `json:"dimensions,omitempty"`
|
| Modalities json.RawMessage `json:"modalities,omitempty"`
|
| Audio json.RawMessage `json:"audio,omitempty"`
|
|
|
|
|
| SafetyIdentifier string `json:"safety_identifier,omitempty"`
|
|
|
|
|
|
|
| Store json.RawMessage `json:"store,omitempty"`
|
|
|
| PromptCacheKey string `json:"prompt_cache_key,omitempty"`
|
| PromptCacheRetention json.RawMessage `json:"prompt_cache_retention,omitempty"`
|
| LogitBias json.RawMessage `json:"logit_bias,omitempty"`
|
| Metadata json.RawMessage `json:"metadata,omitempty"`
|
| Prediction json.RawMessage `json:"prediction,omitempty"`
|
|
|
| ExtraBody json.RawMessage `json:"extra_body,omitempty"`
|
|
|
| SearchParameters json.RawMessage `json:"search_parameters,omitempty"`
|
|
|
| WebSearchOptions *WebSearchOptions `json:"web_search_options,omitempty"`
|
|
|
| Usage json.RawMessage `json:"usage,omitempty"`
|
| Reasoning json.RawMessage `json:"reasoning,omitempty"`
|
|
|
| VlHighResolutionImages json.RawMessage `json:"vl_high_resolution_images,omitempty"`
|
| EnableThinking json.RawMessage `json:"enable_thinking,omitempty"`
|
| ChatTemplateKwargs json.RawMessage `json:"chat_template_kwargs,omitempty"`
|
| EnableSearch json.RawMessage `json:"enable_search,omitempty"`
|
|
|
| Think json.RawMessage `json:"think,omitempty"`
|
|
|
| WebSearch json.RawMessage `json:"web_search,omitempty"`
|
|
|
| THINKING json.RawMessage `json:"thinking,omitempty"`
|
|
|
| SearchDomainFilter json.RawMessage `json:"search_domain_filter,omitempty"`
|
| SearchRecencyFilter string `json:"search_recency_filter,omitempty"`
|
| ReturnImages bool `json:"return_images,omitempty"`
|
| ReturnRelatedQuestions bool `json:"return_related_questions,omitempty"`
|
| SearchMode string `json:"search_mode,omitempty"`
|
| }
|
|
|
| func (r *GeneralOpenAIRequest) GetTokenCountMeta() *types.TokenCountMeta {
|
| var tokenCountMeta types.TokenCountMeta
|
| var texts = make([]string, 0)
|
| var fileMeta = make([]*types.FileMeta, 0)
|
|
|
| if r.Prompt != nil {
|
| switch v := r.Prompt.(type) {
|
| case string:
|
| texts = append(texts, v)
|
| case []any:
|
| for _, item := range v {
|
| if str, ok := item.(string); ok {
|
| texts = append(texts, str)
|
| }
|
| }
|
| default:
|
| texts = append(texts, fmt.Sprintf("%v", r.Prompt))
|
| }
|
| }
|
|
|
| if r.Input != nil {
|
| inputs := r.ParseInput()
|
| texts = append(texts, inputs...)
|
| }
|
|
|
| if r.MaxCompletionTokens > r.MaxTokens {
|
| tokenCountMeta.MaxTokens = int(r.MaxCompletionTokens)
|
| } else {
|
| tokenCountMeta.MaxTokens = int(r.MaxTokens)
|
| }
|
|
|
| for _, message := range r.Messages {
|
| tokenCountMeta.MessagesCount++
|
| texts = append(texts, message.Role)
|
| if message.Content != nil {
|
| if message.Name != nil {
|
| tokenCountMeta.NameCount++
|
| texts = append(texts, *message.Name)
|
| }
|
| arrayContent := message.ParseContent()
|
| for _, m := range arrayContent {
|
| if m.Type == ContentTypeImageURL {
|
| imageUrl := m.GetImageMedia()
|
| if imageUrl != nil {
|
| if imageUrl.Url != "" {
|
| meta := &types.FileMeta{
|
| FileType: types.FileTypeImage,
|
| }
|
| meta.OriginData = imageUrl.Url
|
| meta.Detail = imageUrl.Detail
|
| fileMeta = append(fileMeta, meta)
|
| }
|
| }
|
| } else if m.Type == ContentTypeInputAudio {
|
| inputAudio := m.GetInputAudio()
|
| if inputAudio != nil {
|
| meta := &types.FileMeta{
|
| FileType: types.FileTypeAudio,
|
| }
|
| meta.OriginData = inputAudio.Data
|
| fileMeta = append(fileMeta, meta)
|
| }
|
| } else if m.Type == ContentTypeFile {
|
| file := m.GetFile()
|
| if file != nil {
|
| meta := &types.FileMeta{
|
| FileType: types.FileTypeFile,
|
| }
|
| meta.OriginData = file.FileData
|
| fileMeta = append(fileMeta, meta)
|
| }
|
| } else if m.Type == ContentTypeVideoUrl {
|
| videoUrl := m.GetVideoUrl()
|
| if videoUrl != nil && videoUrl.Url != "" {
|
| meta := &types.FileMeta{
|
| FileType: types.FileTypeVideo,
|
| }
|
| meta.OriginData = videoUrl.Url
|
| fileMeta = append(fileMeta, meta)
|
| }
|
| } else {
|
| texts = append(texts, m.Text)
|
| }
|
| }
|
| }
|
| }
|
|
|
| if r.Tools != nil {
|
| openaiTools := r.Tools
|
| for _, tool := range openaiTools {
|
| tokenCountMeta.ToolsCount++
|
| texts = append(texts, tool.Function.Name)
|
| if tool.Function.Description != "" {
|
| texts = append(texts, tool.Function.Description)
|
| }
|
| if tool.Function.Parameters != nil {
|
| texts = append(texts, fmt.Sprintf("%v", tool.Function.Parameters))
|
| }
|
| }
|
|
|
|
|
|
|
| }
|
| tokenCountMeta.CombineText = strings.Join(texts, "\n")
|
| tokenCountMeta.Files = fileMeta
|
| return &tokenCountMeta
|
| }
|
|
|
| func (r *GeneralOpenAIRequest) IsStream(c *gin.Context) bool {
|
| return r.Stream
|
| }
|
|
|
| func (r *GeneralOpenAIRequest) SetModelName(modelName string) {
|
| if modelName != "" {
|
| r.Model = modelName
|
| }
|
| }
|
|
|
| func (r *GeneralOpenAIRequest) ToMap() map[string]any {
|
| result := make(map[string]any)
|
| data, _ := common.Marshal(r)
|
| _ = common.Unmarshal(data, &result)
|
| return result
|
| }
|
|
|
| func (r *GeneralOpenAIRequest) GetSystemRoleName() string {
|
| if strings.HasPrefix(r.Model, "o") {
|
| if !strings.HasPrefix(r.Model, "o1-mini") && !strings.HasPrefix(r.Model, "o1-preview") {
|
| return "developer"
|
| }
|
| } else if strings.HasPrefix(r.Model, "gpt-5") {
|
| return "developer"
|
| }
|
| return "system"
|
| }
|
|
|
| const CustomType = "custom"
|
|
|
| type ToolCallRequest struct {
|
| ID string `json:"id,omitempty"`
|
| Type string `json:"type"`
|
| Function FunctionRequest `json:"function,omitempty"`
|
| Custom json.RawMessage `json:"custom,omitempty"`
|
| }
|
|
|
| type FunctionRequest struct {
|
| Description string `json:"description,omitempty"`
|
| Name string `json:"name"`
|
| Parameters any `json:"parameters,omitempty"`
|
| Arguments string `json:"arguments,omitempty"`
|
| }
|
|
|
| type StreamOptions struct {
|
| IncludeUsage bool `json:"include_usage,omitempty"`
|
| }
|
|
|
| func (r *GeneralOpenAIRequest) GetMaxTokens() uint {
|
| if r.MaxCompletionTokens != 0 {
|
| return r.MaxCompletionTokens
|
| }
|
| return r.MaxTokens
|
| }
|
|
|
| func (r *GeneralOpenAIRequest) ParseInput() []string {
|
| if r.Input == nil {
|
| return nil
|
| }
|
| var input []string
|
| switch r.Input.(type) {
|
| case string:
|
| input = []string{r.Input.(string)}
|
| case []any:
|
| input = make([]string, 0, len(r.Input.([]any)))
|
| for _, item := range r.Input.([]any) {
|
| if str, ok := item.(string); ok {
|
| input = append(input, str)
|
| }
|
| }
|
| }
|
| return input
|
| }
|
|
|
| type Message struct {
|
| Role string `json:"role"`
|
| Content any `json:"content"`
|
| Name *string `json:"name,omitempty"`
|
| Prefix *bool `json:"prefix,omitempty"`
|
| ReasoningContent string `json:"reasoning_content,omitempty"`
|
| Reasoning string `json:"reasoning,omitempty"`
|
| ToolCalls json.RawMessage `json:"tool_calls,omitempty"`
|
| ToolCallId string `json:"tool_call_id,omitempty"`
|
| parsedContent []MediaContent
|
|
|
| }
|
|
|
| type MediaContent struct {
|
| Type string `json:"type"`
|
| Text string `json:"text,omitempty"`
|
| ImageUrl any `json:"image_url,omitempty"`
|
| InputAudio any `json:"input_audio,omitempty"`
|
| File any `json:"file,omitempty"`
|
| VideoUrl any `json:"video_url,omitempty"`
|
|
|
| CacheControl json.RawMessage `json:"cache_control,omitempty"`
|
| }
|
|
|
| func (m *MediaContent) GetImageMedia() *MessageImageUrl {
|
| if m.ImageUrl != nil {
|
| if _, ok := m.ImageUrl.(*MessageImageUrl); ok {
|
| return m.ImageUrl.(*MessageImageUrl)
|
| }
|
| if itemMap, ok := m.ImageUrl.(map[string]any); ok {
|
| out := &MessageImageUrl{
|
| Url: common.Interface2String(itemMap["url"]),
|
| Detail: common.Interface2String(itemMap["detail"]),
|
| MimeType: common.Interface2String(itemMap["mime_type"]),
|
| }
|
| return out
|
| }
|
| }
|
| return nil
|
| }
|
|
|
| func (m *MediaContent) GetInputAudio() *MessageInputAudio {
|
| if m.InputAudio != nil {
|
| if _, ok := m.InputAudio.(*MessageInputAudio); ok {
|
| return m.InputAudio.(*MessageInputAudio)
|
| }
|
| if itemMap, ok := m.InputAudio.(map[string]any); ok {
|
| out := &MessageInputAudio{
|
| Data: common.Interface2String(itemMap["data"]),
|
| Format: common.Interface2String(itemMap["format"]),
|
| }
|
| return out
|
| }
|
| }
|
| return nil
|
| }
|
|
|
| func (m *MediaContent) GetFile() *MessageFile {
|
| if m.File != nil {
|
| if _, ok := m.File.(*MessageFile); ok {
|
| return m.File.(*MessageFile)
|
| }
|
| if itemMap, ok := m.File.(map[string]any); ok {
|
| out := &MessageFile{
|
| FileName: common.Interface2String(itemMap["file_name"]),
|
| FileData: common.Interface2String(itemMap["file_data"]),
|
| FileId: common.Interface2String(itemMap["file_id"]),
|
| }
|
| return out
|
| }
|
| }
|
| return nil
|
| }
|
|
|
| func (m *MediaContent) GetVideoUrl() *MessageVideoUrl {
|
| if m.VideoUrl != nil {
|
| if _, ok := m.VideoUrl.(*MessageVideoUrl); ok {
|
| return m.VideoUrl.(*MessageVideoUrl)
|
| }
|
| if itemMap, ok := m.VideoUrl.(map[string]any); ok {
|
| out := &MessageVideoUrl{
|
| Url: common.Interface2String(itemMap["url"]),
|
| }
|
| return out
|
| }
|
| }
|
| return nil
|
| }
|
|
|
| type MessageImageUrl struct {
|
| Url string `json:"url"`
|
| Detail string `json:"detail"`
|
| MimeType string
|
| }
|
|
|
| func (m *MessageImageUrl) IsRemoteImage() bool {
|
| return strings.HasPrefix(m.Url, "http")
|
| }
|
|
|
| type MessageInputAudio struct {
|
| Data string `json:"data"`
|
| Format string `json:"format"`
|
| }
|
|
|
| type MessageFile struct {
|
| FileName string `json:"filename,omitempty"`
|
| FileData string `json:"file_data,omitempty"`
|
| FileId string `json:"file_id,omitempty"`
|
| }
|
|
|
| type MessageVideoUrl struct {
|
| Url string `json:"url"`
|
| }
|
|
|
| const (
|
| ContentTypeText = "text"
|
| ContentTypeImageURL = "image_url"
|
| ContentTypeInputAudio = "input_audio"
|
| ContentTypeFile = "file"
|
| ContentTypeVideoUrl = "video_url"
|
|
|
| )
|
|
|
| func (m *Message) GetPrefix() bool {
|
| if m.Prefix == nil {
|
| return false
|
| }
|
| return *m.Prefix
|
| }
|
|
|
| func (m *Message) SetPrefix(prefix bool) {
|
| m.Prefix = &prefix
|
| }
|
|
|
| func (m *Message) ParseToolCalls() []ToolCallRequest {
|
| if m.ToolCalls == nil {
|
| return nil
|
| }
|
| var toolCalls []ToolCallRequest
|
| if err := json.Unmarshal(m.ToolCalls, &toolCalls); err == nil {
|
| return toolCalls
|
| }
|
| return toolCalls
|
| }
|
|
|
| func (m *Message) SetToolCalls(toolCalls any) {
|
| toolCallsJson, _ := json.Marshal(toolCalls)
|
| m.ToolCalls = toolCallsJson
|
| }
|
|
|
| func (m *Message) StringContent() string {
|
| switch m.Content.(type) {
|
| case string:
|
| return m.Content.(string)
|
| case []any:
|
| var contentStr string
|
| for _, contentItem := range m.Content.([]any) {
|
| contentMap, ok := contentItem.(map[string]any)
|
| if !ok {
|
| continue
|
| }
|
| if contentMap["type"] == ContentTypeText {
|
| if subStr, ok := contentMap["text"].(string); ok {
|
| contentStr += subStr
|
| }
|
| }
|
| }
|
| return contentStr
|
| }
|
|
|
| return ""
|
| }
|
|
|
| func (m *Message) SetNullContent() {
|
| m.Content = nil
|
| m.parsedContent = nil
|
| }
|
|
|
| func (m *Message) SetStringContent(content string) {
|
| m.Content = content
|
| m.parsedContent = nil
|
| }
|
|
|
| func (m *Message) SetMediaContent(content []MediaContent) {
|
| m.Content = content
|
| m.parsedContent = content
|
| }
|
|
|
| func (m *Message) IsStringContent() bool {
|
| _, ok := m.Content.(string)
|
| if ok {
|
| return true
|
| }
|
| return false
|
| }
|
|
|
| func (m *Message) ParseContent() []MediaContent {
|
| if m.Content == nil {
|
| return nil
|
| }
|
| if len(m.parsedContent) > 0 {
|
| return m.parsedContent
|
| }
|
|
|
| var contentList []MediaContent
|
|
|
| content, ok := m.Content.(string)
|
| if ok {
|
| contentList = []MediaContent{{
|
| Type: ContentTypeText,
|
| Text: content,
|
| }}
|
| m.parsedContent = contentList
|
| return contentList
|
| }
|
|
|
|
|
|
|
|
|
| arrayContent, ok := m.Content.([]any)
|
| if !ok {
|
| return contentList
|
| }
|
|
|
| for _, contentItemAny := range arrayContent {
|
| mediaItem, ok := contentItemAny.(MediaContent)
|
| if ok {
|
| contentList = append(contentList, mediaItem)
|
| continue
|
| }
|
|
|
| contentItem, ok := contentItemAny.(map[string]any)
|
| if !ok {
|
| continue
|
| }
|
| contentType, ok := contentItem["type"].(string)
|
| if !ok {
|
| continue
|
| }
|
|
|
| switch contentType {
|
| case ContentTypeText:
|
| if text, ok := contentItem["text"].(string); ok {
|
| contentList = append(contentList, MediaContent{
|
| Type: ContentTypeText,
|
| Text: text,
|
| })
|
| }
|
|
|
| case ContentTypeImageURL:
|
| imageUrl := contentItem["image_url"]
|
| temp := &MessageImageUrl{
|
| Detail: "high",
|
| }
|
| switch v := imageUrl.(type) {
|
| case string:
|
| temp.Url = v
|
| case map[string]interface{}:
|
| url, ok1 := v["url"].(string)
|
| detail, ok2 := v["detail"].(string)
|
| if ok2 {
|
| temp.Detail = detail
|
| }
|
| if ok1 {
|
| temp.Url = url
|
| }
|
| }
|
| contentList = append(contentList, MediaContent{
|
| Type: ContentTypeImageURL,
|
| ImageUrl: temp,
|
| })
|
|
|
| case ContentTypeInputAudio:
|
| if audioData, ok := contentItem["input_audio"].(map[string]interface{}); ok {
|
| data, ok1 := audioData["data"].(string)
|
| format, ok2 := audioData["format"].(string)
|
| if ok1 && ok2 {
|
| temp := &MessageInputAudio{
|
| Data: data,
|
| Format: format,
|
| }
|
| contentList = append(contentList, MediaContent{
|
| Type: ContentTypeInputAudio,
|
| InputAudio: temp,
|
| })
|
| }
|
| }
|
| case ContentTypeFile:
|
| if fileData, ok := contentItem["file"].(map[string]interface{}); ok {
|
| fileId, ok3 := fileData["file_id"].(string)
|
| if ok3 {
|
| contentList = append(contentList, MediaContent{
|
| Type: ContentTypeFile,
|
| File: &MessageFile{
|
| FileId: fileId,
|
| },
|
| })
|
| } else {
|
| fileName, ok1 := fileData["filename"].(string)
|
| fileDataStr, ok2 := fileData["file_data"].(string)
|
| if ok1 && ok2 {
|
| contentList = append(contentList, MediaContent{
|
| Type: ContentTypeFile,
|
| File: &MessageFile{
|
| FileName: fileName,
|
| FileData: fileDataStr,
|
| },
|
| })
|
| }
|
| }
|
| }
|
| case ContentTypeVideoUrl:
|
| if videoUrl, ok := contentItem["video_url"].(string); ok {
|
| contentList = append(contentList, MediaContent{
|
| Type: ContentTypeVideoUrl,
|
| VideoUrl: &MessageVideoUrl{
|
| Url: videoUrl,
|
| },
|
| })
|
| }
|
| }
|
| }
|
|
|
| if len(contentList) > 0 {
|
| m.parsedContent = contentList
|
| }
|
| return contentList
|
| }
|
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| type WebSearchOptions struct {
|
| SearchContextSize string `json:"search_context_size,omitempty"`
|
| UserLocation json.RawMessage `json:"user_location,omitempty"`
|
| }
|
|
|
|
|
| type OpenAIResponsesRequest struct {
|
| Model string `json:"model"`
|
| Input json.RawMessage `json:"input,omitempty"`
|
| Include json.RawMessage `json:"include,omitempty"`
|
| Instructions json.RawMessage `json:"instructions,omitempty"`
|
| MaxOutputTokens uint `json:"max_output_tokens,omitempty"`
|
| Metadata json.RawMessage `json:"metadata,omitempty"`
|
| ParallelToolCalls json.RawMessage `json:"parallel_tool_calls,omitempty"`
|
| PreviousResponseID string `json:"previous_response_id,omitempty"`
|
| Reasoning *Reasoning `json:"reasoning,omitempty"`
|
|
|
| ServiceTier string `json:"service_tier,omitempty"`
|
| Store json.RawMessage `json:"store,omitempty"`
|
| PromptCacheKey json.RawMessage `json:"prompt_cache_key,omitempty"`
|
| PromptCacheRetention json.RawMessage `json:"prompt_cache_retention,omitempty"`
|
| Stream bool `json:"stream,omitempty"`
|
| Temperature *float64 `json:"temperature,omitempty"`
|
| Text json.RawMessage `json:"text,omitempty"`
|
| ToolChoice json.RawMessage `json:"tool_choice,omitempty"`
|
| Tools json.RawMessage `json:"tools,omitempty"`
|
| TopP *float64 `json:"top_p,omitempty"`
|
| Truncation string `json:"truncation,omitempty"`
|
| User string `json:"user,omitempty"`
|
| MaxToolCalls uint `json:"max_tool_calls,omitempty"`
|
| Prompt json.RawMessage `json:"prompt,omitempty"`
|
|
|
| EnableThinking json.RawMessage `json:"enable_thinking,omitempty"`
|
|
|
| Preset json.RawMessage `json:"preset,omitempty"`
|
| }
|
|
|
| func (r *OpenAIResponsesRequest) GetTokenCountMeta() *types.TokenCountMeta {
|
| var fileMeta = make([]*types.FileMeta, 0)
|
| var texts = make([]string, 0)
|
|
|
| if r.Input != nil {
|
| inputs := r.ParseInput()
|
| for _, input := range inputs {
|
| if input.Type == "input_image" {
|
| if input.ImageUrl != "" {
|
| fileMeta = append(fileMeta, &types.FileMeta{
|
| FileType: types.FileTypeImage,
|
| OriginData: input.ImageUrl,
|
| Detail: input.Detail,
|
| })
|
| }
|
| } else if input.Type == "input_file" {
|
| if input.FileUrl != "" {
|
| fileMeta = append(fileMeta, &types.FileMeta{
|
| FileType: types.FileTypeFile,
|
| OriginData: input.FileUrl,
|
| })
|
| }
|
| } else {
|
| texts = append(texts, input.Text)
|
| }
|
| }
|
| }
|
|
|
| if len(r.Instructions) > 0 {
|
| texts = append(texts, string(r.Instructions))
|
| }
|
|
|
| if len(r.Metadata) > 0 {
|
| texts = append(texts, string(r.Metadata))
|
| }
|
|
|
| if len(r.Text) > 0 {
|
| texts = append(texts, string(r.Text))
|
| }
|
|
|
| if len(r.ToolChoice) > 0 {
|
| texts = append(texts, string(r.ToolChoice))
|
| }
|
|
|
| if len(r.Prompt) > 0 {
|
| texts = append(texts, string(r.Prompt))
|
| }
|
|
|
| if len(r.Tools) > 0 {
|
| texts = append(texts, string(r.Tools))
|
| }
|
|
|
| return &types.TokenCountMeta{
|
| CombineText: strings.Join(texts, "\n"),
|
| Files: fileMeta,
|
| MaxTokens: int(r.MaxOutputTokens),
|
| }
|
| }
|
|
|
| func (r *OpenAIResponsesRequest) IsStream(c *gin.Context) bool {
|
| return r.Stream
|
| }
|
|
|
| func (r *OpenAIResponsesRequest) SetModelName(modelName string) {
|
| if modelName != "" {
|
| r.Model = modelName
|
| }
|
| }
|
|
|
| func (r *OpenAIResponsesRequest) GetToolsMap() []map[string]any {
|
| var toolsMap []map[string]any
|
| if len(r.Tools) > 0 {
|
| _ = common.Unmarshal(r.Tools, &toolsMap)
|
| }
|
| return toolsMap
|
| }
|
|
|
| type Reasoning struct {
|
| Effort string `json:"effort,omitempty"`
|
| Summary string `json:"summary,omitempty"`
|
| }
|
|
|
| type Input struct {
|
| Type string `json:"type,omitempty"`
|
| Role string `json:"role,omitempty"`
|
| Content json.RawMessage `json:"content,omitempty"`
|
| }
|
|
|
| type MediaInput struct {
|
| Type string `json:"type"`
|
| Text string `json:"text,omitempty"`
|
| FileUrl string `json:"file_url,omitempty"`
|
| ImageUrl string `json:"image_url,omitempty"`
|
| Detail string `json:"detail,omitempty"`
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
| func (r *OpenAIResponsesRequest) ParseInput() []MediaInput {
|
| if r.Input == nil {
|
| return nil
|
| }
|
|
|
| var mediaInputs []MediaInput
|
|
|
|
|
|
|
|
|
|
|
|
|
| if common.GetJsonType(r.Input) == "string" {
|
| var str string
|
| _ = common.Unmarshal(r.Input, &str)
|
| mediaInputs = append(mediaInputs, MediaInput{Type: "input_text", Text: str})
|
| return mediaInputs
|
| }
|
|
|
|
|
| if common.GetJsonType(r.Input) == "array" {
|
| var inputs []Input
|
| _ = common.Unmarshal(r.Input, &inputs)
|
| for _, input := range inputs {
|
| if common.GetJsonType(input.Content) == "string" {
|
| var str string
|
| _ = common.Unmarshal(input.Content, &str)
|
| mediaInputs = append(mediaInputs, MediaInput{Type: "input_text", Text: str})
|
| }
|
|
|
| if common.GetJsonType(input.Content) == "array" {
|
| var array []any
|
| _ = common.Unmarshal(input.Content, &array)
|
| for _, itemAny := range array {
|
|
|
| if media, ok := itemAny.(MediaInput); ok {
|
| mediaInputs = append(mediaInputs, media)
|
| continue
|
| }
|
|
|
|
|
| item, ok := itemAny.(map[string]any)
|
| if !ok {
|
| continue
|
| }
|
|
|
| typeVal, ok := item["type"].(string)
|
| if !ok {
|
| continue
|
| }
|
| switch typeVal {
|
| case "input_text":
|
| text, _ := item["text"].(string)
|
| mediaInputs = append(mediaInputs, MediaInput{Type: "input_text", Text: text})
|
| case "input_image":
|
|
|
| var imageUrl string
|
| switch v := item["image_url"].(type) {
|
| case string:
|
| imageUrl = v
|
| case map[string]any:
|
| if url, ok := v["url"].(string); ok {
|
| imageUrl = url
|
| }
|
| }
|
| mediaInputs = append(mediaInputs, MediaInput{Type: "input_image", ImageUrl: imageUrl})
|
| case "input_file":
|
|
|
| var fileUrl string
|
| switch v := item["file_url"].(type) {
|
| case string:
|
| fileUrl = v
|
| case map[string]any:
|
| if url, ok := v["url"].(string); ok {
|
| fileUrl = url
|
| }
|
| }
|
| mediaInputs = append(mediaInputs, MediaInput{Type: "input_file", FileUrl: fileUrl})
|
| }
|
| }
|
| }
|
| }
|
| }
|
|
|
| return mediaInputs
|
| }
|
|
|