openai_request.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. package dto
  2. import (
  3. "encoding/json"
  4. "one-api/common"
  5. "strings"
  6. )
  7. type ResponseFormat struct {
  8. Type string `json:"type,omitempty"`
  9. JsonSchema *FormatJsonSchema `json:"json_schema,omitempty"`
  10. }
  11. type FormatJsonSchema struct {
  12. Description string `json:"description,omitempty"`
  13. Name string `json:"name"`
  14. Schema any `json:"schema,omitempty"`
  15. Strict any `json:"strict,omitempty"`
  16. }
  17. type GeneralOpenAIRequest struct {
  18. Model string `json:"model,omitempty"`
  19. Messages []Message `json:"messages,omitempty"`
  20. Prompt any `json:"prompt,omitempty"`
  21. Prefix any `json:"prefix,omitempty"`
  22. Suffix any `json:"suffix,omitempty"`
  23. Stream bool `json:"stream,omitempty"`
  24. StreamOptions *StreamOptions `json:"stream_options,omitempty"`
  25. MaxTokens uint `json:"max_tokens,omitempty"`
  26. MaxCompletionTokens uint `json:"max_completion_tokens,omitempty"`
  27. ReasoningEffort string `json:"reasoning_effort,omitempty"`
  28. Temperature *float64 `json:"temperature,omitempty"`
  29. TopP float64 `json:"top_p,omitempty"`
  30. TopK int `json:"top_k,omitempty"`
  31. Stop any `json:"stop,omitempty"`
  32. N int `json:"n,omitempty"`
  33. Input any `json:"input,omitempty"`
  34. Instruction string `json:"instruction,omitempty"`
  35. Size string `json:"size,omitempty"`
  36. Functions json.RawMessage `json:"functions,omitempty"`
  37. FrequencyPenalty float64 `json:"frequency_penalty,omitempty"`
  38. PresencePenalty float64 `json:"presence_penalty,omitempty"`
  39. ResponseFormat *ResponseFormat `json:"response_format,omitempty"`
  40. EncodingFormat json.RawMessage `json:"encoding_format,omitempty"`
  41. Seed float64 `json:"seed,omitempty"`
  42. ParallelTooCalls *bool `json:"parallel_tool_calls,omitempty"`
  43. Tools []ToolCallRequest `json:"tools,omitempty"`
  44. ToolChoice any `json:"tool_choice,omitempty"`
  45. User string `json:"user,omitempty"`
  46. LogProbs bool `json:"logprobs,omitempty"`
  47. TopLogProbs int `json:"top_logprobs,omitempty"`
  48. Dimensions int `json:"dimensions,omitempty"`
  49. Modalities json.RawMessage `json:"modalities,omitempty"`
  50. Audio json.RawMessage `json:"audio,omitempty"`
  51. EnableThinking any `json:"enable_thinking,omitempty"` // ali
  52. THINKING json.RawMessage `json:"thinking,omitempty"` // doubao
  53. ExtraBody json.RawMessage `json:"extra_body,omitempty"`
  54. WebSearchOptions *WebSearchOptions `json:"web_search_options,omitempty"`
  55. // OpenRouter Params
  56. Usage json.RawMessage `json:"usage,omitempty"`
  57. Reasoning json.RawMessage `json:"reasoning,omitempty"`
  58. // Ali Qwen Params
  59. VlHighResolutionImages json.RawMessage `json:"vl_high_resolution_images,omitempty"`
  60. }
  61. func (r *GeneralOpenAIRequest) ToMap() map[string]any {
  62. result := make(map[string]any)
  63. data, _ := common.Marshal(r)
  64. _ = common.Unmarshal(data, &result)
  65. return result
  66. }
  67. type ToolCallRequest struct {
  68. ID string `json:"id,omitempty"`
  69. Type string `json:"type"`
  70. Function FunctionRequest `json:"function"`
  71. }
  72. type FunctionRequest struct {
  73. Description string `json:"description,omitempty"`
  74. Name string `json:"name"`
  75. Parameters any `json:"parameters,omitempty"`
  76. Arguments string `json:"arguments,omitempty"`
  77. }
  78. type StreamOptions struct {
  79. IncludeUsage bool `json:"include_usage,omitempty"`
  80. }
  81. func (r *GeneralOpenAIRequest) GetMaxTokens() int {
  82. return int(r.MaxTokens)
  83. }
  84. func (r *GeneralOpenAIRequest) ParseInput() []string {
  85. if r.Input == nil {
  86. return nil
  87. }
  88. var input []string
  89. switch r.Input.(type) {
  90. case string:
  91. input = []string{r.Input.(string)}
  92. case []any:
  93. input = make([]string, 0, len(r.Input.([]any)))
  94. for _, item := range r.Input.([]any) {
  95. if str, ok := item.(string); ok {
  96. input = append(input, str)
  97. }
  98. }
  99. }
  100. return input
  101. }
  102. type Message struct {
  103. Role string `json:"role"`
  104. Content any `json:"content"`
  105. Name *string `json:"name,omitempty"`
  106. Prefix *bool `json:"prefix,omitempty"`
  107. ReasoningContent string `json:"reasoning_content,omitempty"`
  108. Reasoning string `json:"reasoning,omitempty"`
  109. ToolCalls json.RawMessage `json:"tool_calls,omitempty"`
  110. ToolCallId string `json:"tool_call_id,omitempty"`
  111. parsedContent []MediaContent
  112. //parsedStringContent *string
  113. }
  114. type MediaContent struct {
  115. Type string `json:"type"`
  116. Text string `json:"text,omitempty"`
  117. ImageUrl any `json:"image_url,omitempty"`
  118. InputAudio any `json:"input_audio,omitempty"`
  119. File any `json:"file,omitempty"`
  120. VideoUrl any `json:"video_url,omitempty"`
  121. // OpenRouter Params
  122. CacheControl json.RawMessage `json:"cache_control,omitempty"`
  123. }
  124. func (m *MediaContent) GetImageMedia() *MessageImageUrl {
  125. if m.ImageUrl != nil {
  126. if _, ok := m.ImageUrl.(*MessageImageUrl); ok {
  127. return m.ImageUrl.(*MessageImageUrl)
  128. }
  129. if itemMap, ok := m.ImageUrl.(map[string]any); ok {
  130. out := &MessageImageUrl{
  131. Url: common.Interface2String(itemMap["url"]),
  132. Detail: common.Interface2String(itemMap["detail"]),
  133. MimeType: common.Interface2String(itemMap["mime_type"]),
  134. }
  135. return out
  136. }
  137. }
  138. return nil
  139. }
  140. func (m *MediaContent) GetInputAudio() *MessageInputAudio {
  141. if m.InputAudio != nil {
  142. if _, ok := m.InputAudio.(*MessageInputAudio); ok {
  143. return m.InputAudio.(*MessageInputAudio)
  144. }
  145. if itemMap, ok := m.InputAudio.(map[string]any); ok {
  146. out := &MessageInputAudio{
  147. Data: common.Interface2String(itemMap["data"]),
  148. Format: common.Interface2String(itemMap["format"]),
  149. }
  150. return out
  151. }
  152. }
  153. return nil
  154. }
  155. func (m *MediaContent) GetFile() *MessageFile {
  156. if m.File != nil {
  157. if _, ok := m.File.(*MessageFile); ok {
  158. return m.File.(*MessageFile)
  159. }
  160. if itemMap, ok := m.File.(map[string]any); ok {
  161. out := &MessageFile{
  162. FileName: common.Interface2String(itemMap["file_name"]),
  163. FileData: common.Interface2String(itemMap["file_data"]),
  164. FileId: common.Interface2String(itemMap["file_id"]),
  165. }
  166. return out
  167. }
  168. }
  169. return nil
  170. }
  171. type MessageImageUrl struct {
  172. Url string `json:"url"`
  173. Detail string `json:"detail"`
  174. MimeType string
  175. }
  176. func (m *MessageImageUrl) IsRemoteImage() bool {
  177. return strings.HasPrefix(m.Url, "http")
  178. }
  179. type MessageInputAudio struct {
  180. Data string `json:"data"` //base64
  181. Format string `json:"format"`
  182. }
  183. type MessageFile struct {
  184. FileName string `json:"filename,omitempty"`
  185. FileData string `json:"file_data,omitempty"`
  186. FileId string `json:"file_id,omitempty"`
  187. }
  188. type MessageVideoUrl struct {
  189. Url string `json:"url"`
  190. }
  191. const (
  192. ContentTypeText = "text"
  193. ContentTypeImageURL = "image_url"
  194. ContentTypeInputAudio = "input_audio"
  195. ContentTypeFile = "file"
  196. ContentTypeVideoUrl = "video_url" // 阿里百炼视频识别
  197. )
  198. func (m *Message) GetPrefix() bool {
  199. if m.Prefix == nil {
  200. return false
  201. }
  202. return *m.Prefix
  203. }
  204. func (m *Message) SetPrefix(prefix bool) {
  205. m.Prefix = &prefix
  206. }
  207. func (m *Message) ParseToolCalls() []ToolCallRequest {
  208. if m.ToolCalls == nil {
  209. return nil
  210. }
  211. var toolCalls []ToolCallRequest
  212. if err := json.Unmarshal(m.ToolCalls, &toolCalls); err == nil {
  213. return toolCalls
  214. }
  215. return toolCalls
  216. }
  217. func (m *Message) SetToolCalls(toolCalls any) {
  218. toolCallsJson, _ := json.Marshal(toolCalls)
  219. m.ToolCalls = toolCallsJson
  220. }
  221. func (m *Message) StringContent() string {
  222. switch m.Content.(type) {
  223. case string:
  224. return m.Content.(string)
  225. case []any:
  226. var contentStr string
  227. for _, contentItem := range m.Content.([]any) {
  228. contentMap, ok := contentItem.(map[string]any)
  229. if !ok {
  230. continue
  231. }
  232. if contentMap["type"] == ContentTypeText {
  233. if subStr, ok := contentMap["text"].(string); ok {
  234. contentStr += subStr
  235. }
  236. }
  237. }
  238. return contentStr
  239. }
  240. return ""
  241. }
  242. func (m *Message) SetNullContent() {
  243. m.Content = nil
  244. m.parsedContent = nil
  245. }
  246. func (m *Message) SetStringContent(content string) {
  247. m.Content = content
  248. m.parsedContent = nil
  249. }
  250. func (m *Message) SetMediaContent(content []MediaContent) {
  251. m.Content = content
  252. m.parsedContent = content
  253. }
  254. func (m *Message) IsStringContent() bool {
  255. _, ok := m.Content.(string)
  256. if ok {
  257. return true
  258. }
  259. return false
  260. }
  261. func (m *Message) ParseContent() []MediaContent {
  262. if m.Content == nil {
  263. return nil
  264. }
  265. if len(m.parsedContent) > 0 {
  266. return m.parsedContent
  267. }
  268. var contentList []MediaContent
  269. // 先尝试解析为字符串
  270. content, ok := m.Content.(string)
  271. if ok {
  272. contentList = []MediaContent{{
  273. Type: ContentTypeText,
  274. Text: content,
  275. }}
  276. m.parsedContent = contentList
  277. return contentList
  278. }
  279. // 尝试解析为数组
  280. //var arrayContent []map[string]interface{}
  281. arrayContent, ok := m.Content.([]any)
  282. if !ok {
  283. return contentList
  284. }
  285. for _, contentItemAny := range arrayContent {
  286. mediaItem, ok := contentItemAny.(MediaContent)
  287. if ok {
  288. contentList = append(contentList, mediaItem)
  289. continue
  290. }
  291. contentItem, ok := contentItemAny.(map[string]any)
  292. if !ok {
  293. continue
  294. }
  295. contentType, ok := contentItem["type"].(string)
  296. if !ok {
  297. continue
  298. }
  299. switch contentType {
  300. case ContentTypeText:
  301. if text, ok := contentItem["text"].(string); ok {
  302. contentList = append(contentList, MediaContent{
  303. Type: ContentTypeText,
  304. Text: text,
  305. })
  306. }
  307. case ContentTypeImageURL:
  308. imageUrl := contentItem["image_url"]
  309. temp := &MessageImageUrl{
  310. Detail: "high",
  311. }
  312. switch v := imageUrl.(type) {
  313. case string:
  314. temp.Url = v
  315. case map[string]interface{}:
  316. url, ok1 := v["url"].(string)
  317. detail, ok2 := v["detail"].(string)
  318. if ok2 {
  319. temp.Detail = detail
  320. }
  321. if ok1 {
  322. temp.Url = url
  323. }
  324. }
  325. contentList = append(contentList, MediaContent{
  326. Type: ContentTypeImageURL,
  327. ImageUrl: temp,
  328. })
  329. case ContentTypeInputAudio:
  330. if audioData, ok := contentItem["input_audio"].(map[string]interface{}); ok {
  331. data, ok1 := audioData["data"].(string)
  332. format, ok2 := audioData["format"].(string)
  333. if ok1 && ok2 {
  334. temp := &MessageInputAudio{
  335. Data: data,
  336. Format: format,
  337. }
  338. contentList = append(contentList, MediaContent{
  339. Type: ContentTypeInputAudio,
  340. InputAudio: temp,
  341. })
  342. }
  343. }
  344. case ContentTypeFile:
  345. if fileData, ok := contentItem["file"].(map[string]interface{}); ok {
  346. fileId, ok3 := fileData["file_id"].(string)
  347. if ok3 {
  348. contentList = append(contentList, MediaContent{
  349. Type: ContentTypeFile,
  350. File: &MessageFile{
  351. FileId: fileId,
  352. },
  353. })
  354. } else {
  355. fileName, ok1 := fileData["filename"].(string)
  356. fileDataStr, ok2 := fileData["file_data"].(string)
  357. if ok1 && ok2 {
  358. contentList = append(contentList, MediaContent{
  359. Type: ContentTypeFile,
  360. File: &MessageFile{
  361. FileName: fileName,
  362. FileData: fileDataStr,
  363. },
  364. })
  365. }
  366. }
  367. }
  368. case ContentTypeVideoUrl:
  369. if videoUrl, ok := contentItem["video_url"].(string); ok {
  370. contentList = append(contentList, MediaContent{
  371. Type: ContentTypeVideoUrl,
  372. VideoUrl: &MessageVideoUrl{
  373. Url: videoUrl,
  374. },
  375. })
  376. }
  377. }
  378. }
  379. if len(contentList) > 0 {
  380. m.parsedContent = contentList
  381. }
  382. return contentList
  383. }
  384. // old code
  385. /*func (m *Message) StringContent() string {
  386. if m.parsedStringContent != nil {
  387. return *m.parsedStringContent
  388. }
  389. var stringContent string
  390. if err := json.Unmarshal(m.Content, &stringContent); err == nil {
  391. m.parsedStringContent = &stringContent
  392. return stringContent
  393. }
  394. contentStr := new(strings.Builder)
  395. arrayContent := m.ParseContent()
  396. for _, content := range arrayContent {
  397. if content.Type == ContentTypeText {
  398. contentStr.WriteString(content.Text)
  399. }
  400. }
  401. stringContent = contentStr.String()
  402. m.parsedStringContent = &stringContent
  403. return stringContent
  404. }
  405. func (m *Message) SetNullContent() {
  406. m.Content = nil
  407. m.parsedStringContent = nil
  408. m.parsedContent = nil
  409. }
  410. func (m *Message) SetStringContent(content string) {
  411. jsonContent, _ := json.Marshal(content)
  412. m.Content = jsonContent
  413. m.parsedStringContent = &content
  414. m.parsedContent = nil
  415. }
  416. func (m *Message) SetMediaContent(content []MediaContent) {
  417. jsonContent, _ := json.Marshal(content)
  418. m.Content = jsonContent
  419. m.parsedContent = nil
  420. m.parsedStringContent = nil
  421. }
  422. func (m *Message) IsStringContent() bool {
  423. if m.parsedStringContent != nil {
  424. return true
  425. }
  426. var stringContent string
  427. if err := json.Unmarshal(m.Content, &stringContent); err == nil {
  428. m.parsedStringContent = &stringContent
  429. return true
  430. }
  431. return false
  432. }
  433. func (m *Message) ParseContent() []MediaContent {
  434. if m.parsedContent != nil {
  435. return m.parsedContent
  436. }
  437. var contentList []MediaContent
  438. // 先尝试解析为字符串
  439. var stringContent string
  440. if err := json.Unmarshal(m.Content, &stringContent); err == nil {
  441. contentList = []MediaContent{{
  442. Type: ContentTypeText,
  443. Text: stringContent,
  444. }}
  445. m.parsedContent = contentList
  446. return contentList
  447. }
  448. // 尝试解析为数组
  449. var arrayContent []map[string]interface{}
  450. if err := json.Unmarshal(m.Content, &arrayContent); err == nil {
  451. for _, contentItem := range arrayContent {
  452. contentType, ok := contentItem["type"].(string)
  453. if !ok {
  454. continue
  455. }
  456. switch contentType {
  457. case ContentTypeText:
  458. if text, ok := contentItem["text"].(string); ok {
  459. contentList = append(contentList, MediaContent{
  460. Type: ContentTypeText,
  461. Text: text,
  462. })
  463. }
  464. case ContentTypeImageURL:
  465. imageUrl := contentItem["image_url"]
  466. temp := &MessageImageUrl{
  467. Detail: "high",
  468. }
  469. switch v := imageUrl.(type) {
  470. case string:
  471. temp.Url = v
  472. case map[string]interface{}:
  473. url, ok1 := v["url"].(string)
  474. detail, ok2 := v["detail"].(string)
  475. if ok2 {
  476. temp.Detail = detail
  477. }
  478. if ok1 {
  479. temp.Url = url
  480. }
  481. }
  482. contentList = append(contentList, MediaContent{
  483. Type: ContentTypeImageURL,
  484. ImageUrl: temp,
  485. })
  486. case ContentTypeInputAudio:
  487. if audioData, ok := contentItem["input_audio"].(map[string]interface{}); ok {
  488. data, ok1 := audioData["data"].(string)
  489. format, ok2 := audioData["format"].(string)
  490. if ok1 && ok2 {
  491. temp := &MessageInputAudio{
  492. Data: data,
  493. Format: format,
  494. }
  495. contentList = append(contentList, MediaContent{
  496. Type: ContentTypeInputAudio,
  497. InputAudio: temp,
  498. })
  499. }
  500. }
  501. case ContentTypeFile:
  502. if fileData, ok := contentItem["file"].(map[string]interface{}); ok {
  503. fileId, ok3 := fileData["file_id"].(string)
  504. if ok3 {
  505. contentList = append(contentList, MediaContent{
  506. Type: ContentTypeFile,
  507. File: &MessageFile{
  508. FileId: fileId,
  509. },
  510. })
  511. } else {
  512. fileName, ok1 := fileData["filename"].(string)
  513. fileDataStr, ok2 := fileData["file_data"].(string)
  514. if ok1 && ok2 {
  515. contentList = append(contentList, MediaContent{
  516. Type: ContentTypeFile,
  517. File: &MessageFile{
  518. FileName: fileName,
  519. FileData: fileDataStr,
  520. },
  521. })
  522. }
  523. }
  524. }
  525. case ContentTypeVideoUrl:
  526. if videoUrl, ok := contentItem["video_url"].(string); ok {
  527. contentList = append(contentList, MediaContent{
  528. Type: ContentTypeVideoUrl,
  529. VideoUrl: &MessageVideoUrl{
  530. Url: videoUrl,
  531. },
  532. })
  533. }
  534. }
  535. }
  536. }
  537. if len(contentList) > 0 {
  538. m.parsedContent = contentList
  539. }
  540. return contentList
  541. }*/
  542. type WebSearchOptions struct {
  543. SearchContextSize string `json:"search_context_size,omitempty"`
  544. UserLocation json.RawMessage `json:"user_location,omitempty"`
  545. }
  546. type OpenAIResponsesRequest struct {
  547. Model string `json:"model"`
  548. Input json.RawMessage `json:"input,omitempty"`
  549. Include json.RawMessage `json:"include,omitempty"`
  550. Instructions json.RawMessage `json:"instructions,omitempty"`
  551. MaxOutputTokens uint `json:"max_output_tokens,omitempty"`
  552. Metadata json.RawMessage `json:"metadata,omitempty"`
  553. ParallelToolCalls bool `json:"parallel_tool_calls,omitempty"`
  554. PreviousResponseID string `json:"previous_response_id,omitempty"`
  555. Reasoning *Reasoning `json:"reasoning,omitempty"`
  556. ServiceTier string `json:"service_tier,omitempty"`
  557. Store bool `json:"store,omitempty"`
  558. Stream bool `json:"stream,omitempty"`
  559. Temperature float64 `json:"temperature,omitempty"`
  560. Text json.RawMessage `json:"text,omitempty"`
  561. ToolChoice json.RawMessage `json:"tool_choice,omitempty"`
  562. Tools []ResponsesToolsCall `json:"tools,omitempty"`
  563. TopP float64 `json:"top_p,omitempty"`
  564. Truncation string `json:"truncation,omitempty"`
  565. User string `json:"user,omitempty"`
  566. }
  567. type Reasoning struct {
  568. Effort string `json:"effort,omitempty"`
  569. Summary string `json:"summary,omitempty"`
  570. }
  571. type ResponsesToolsCall struct {
  572. Type string `json:"type"`
  573. // Web Search
  574. UserLocation json.RawMessage `json:"user_location,omitempty"`
  575. SearchContextSize string `json:"search_context_size,omitempty"`
  576. // File Search
  577. VectorStoreIds []string `json:"vector_store_ids,omitempty"`
  578. MaxNumResults uint `json:"max_num_results,omitempty"`
  579. Filters json.RawMessage `json:"filters,omitempty"`
  580. // Computer Use
  581. DisplayWidth uint `json:"display_width,omitempty"`
  582. DisplayHeight uint `json:"display_height,omitempty"`
  583. Environment string `json:"environment,omitempty"`
  584. // Function
  585. Name string `json:"name,omitempty"`
  586. Description string `json:"description,omitempty"`
  587. Parameters json.RawMessage `json:"parameters,omitempty"`
  588. Function json.RawMessage `json:"function,omitempty"`
  589. Container json.RawMessage `json:"container,omitempty"`
  590. }