openai_json_optimization_benchmark_test.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. package service
  2. import (
  3. "encoding/json"
  4. "strconv"
  5. "strings"
  6. "testing"
  7. "github.com/tidwall/gjson"
  8. )
  9. var (
  10. benchmarkToolContinuationBoolSink bool
  11. benchmarkWSParseStringSink string
  12. benchmarkWSParseMapSink map[string]any
  13. benchmarkUsageSink OpenAIUsage
  14. )
  15. func BenchmarkToolContinuationValidationLegacy(b *testing.B) {
  16. reqBody := benchmarkToolContinuationRequestBody()
  17. b.ReportAllocs()
  18. b.ResetTimer()
  19. for i := 0; i < b.N; i++ {
  20. benchmarkToolContinuationBoolSink = legacyValidateFunctionCallOutputContext(reqBody)
  21. }
  22. }
  23. func BenchmarkToolContinuationValidationOptimized(b *testing.B) {
  24. reqBody := benchmarkToolContinuationRequestBody()
  25. b.ReportAllocs()
  26. b.ResetTimer()
  27. for i := 0; i < b.N; i++ {
  28. benchmarkToolContinuationBoolSink = optimizedValidateFunctionCallOutputContext(reqBody)
  29. }
  30. }
  31. func BenchmarkWSIngressPayloadParseLegacy(b *testing.B) {
  32. raw := benchmarkWSIngressPayloadBytes()
  33. b.ReportAllocs()
  34. b.ResetTimer()
  35. for i := 0; i < b.N; i++ {
  36. eventType, model, promptCacheKey, previousResponseID, payload, err := legacyParseWSIngressPayload(raw)
  37. if err == nil {
  38. benchmarkWSParseStringSink = eventType + model + promptCacheKey + previousResponseID
  39. benchmarkWSParseMapSink = payload
  40. }
  41. }
  42. }
  43. func BenchmarkWSIngressPayloadParseOptimized(b *testing.B) {
  44. raw := benchmarkWSIngressPayloadBytes()
  45. b.ReportAllocs()
  46. b.ResetTimer()
  47. for i := 0; i < b.N; i++ {
  48. eventType, model, promptCacheKey, previousResponseID, payload, err := optimizedParseWSIngressPayload(raw)
  49. if err == nil {
  50. benchmarkWSParseStringSink = eventType + model + promptCacheKey + previousResponseID
  51. benchmarkWSParseMapSink = payload
  52. }
  53. }
  54. }
  55. func BenchmarkOpenAIUsageExtractLegacy(b *testing.B) {
  56. body := benchmarkOpenAIUsageJSONBytes()
  57. b.ReportAllocs()
  58. b.ResetTimer()
  59. for i := 0; i < b.N; i++ {
  60. usage, ok := legacyExtractOpenAIUsageFromJSONBytes(body)
  61. if ok {
  62. benchmarkUsageSink = usage
  63. }
  64. }
  65. }
  66. func BenchmarkOpenAIUsageExtractOptimized(b *testing.B) {
  67. body := benchmarkOpenAIUsageJSONBytes()
  68. b.ReportAllocs()
  69. b.ResetTimer()
  70. for i := 0; i < b.N; i++ {
  71. usage, ok := extractOpenAIUsageFromJSONBytes(body)
  72. if ok {
  73. benchmarkUsageSink = usage
  74. }
  75. }
  76. }
  77. func benchmarkToolContinuationRequestBody() map[string]any {
  78. input := make([]any, 0, 64)
  79. for i := 0; i < 24; i++ {
  80. input = append(input, map[string]any{
  81. "type": "text",
  82. "text": "benchmark text",
  83. })
  84. }
  85. for i := 0; i < 10; i++ {
  86. callID := "call_" + strconv.Itoa(i)
  87. input = append(input, map[string]any{
  88. "type": "tool_call",
  89. "call_id": callID,
  90. })
  91. input = append(input, map[string]any{
  92. "type": "function_call_output",
  93. "call_id": callID,
  94. })
  95. input = append(input, map[string]any{
  96. "type": "item_reference",
  97. "id": callID,
  98. })
  99. }
  100. return map[string]any{
  101. "model": "gpt-5.3-codex",
  102. "input": input,
  103. }
  104. }
  105. func benchmarkWSIngressPayloadBytes() []byte {
  106. return []byte(`{"type":"response.create","model":"gpt-5.3-codex","prompt_cache_key":"cache_bench","previous_response_id":"resp_prev_bench","input":[{"type":"message","role":"user","content":[{"type":"input_text","text":"hello"}]}]}`)
  107. }
  108. func benchmarkOpenAIUsageJSONBytes() []byte {
  109. return []byte(`{"id":"resp_bench","object":"response","model":"gpt-5.3-codex","usage":{"input_tokens":3210,"output_tokens":987,"input_tokens_details":{"cached_tokens":456}}}`)
  110. }
  111. func legacyValidateFunctionCallOutputContext(reqBody map[string]any) bool {
  112. if !legacyHasFunctionCallOutput(reqBody) {
  113. return true
  114. }
  115. previousResponseID, _ := reqBody["previous_response_id"].(string)
  116. if strings.TrimSpace(previousResponseID) != "" {
  117. return true
  118. }
  119. if legacyHasToolCallContext(reqBody) {
  120. return true
  121. }
  122. if legacyHasFunctionCallOutputMissingCallID(reqBody) {
  123. return false
  124. }
  125. callIDs := legacyFunctionCallOutputCallIDs(reqBody)
  126. return legacyHasItemReferenceForCallIDs(reqBody, callIDs)
  127. }
  128. func optimizedValidateFunctionCallOutputContext(reqBody map[string]any) bool {
  129. validation := ValidateFunctionCallOutputContext(reqBody)
  130. if !validation.HasFunctionCallOutput {
  131. return true
  132. }
  133. previousResponseID, _ := reqBody["previous_response_id"].(string)
  134. if strings.TrimSpace(previousResponseID) != "" {
  135. return true
  136. }
  137. if validation.HasToolCallContext {
  138. return true
  139. }
  140. if validation.HasFunctionCallOutputMissingCallID {
  141. return false
  142. }
  143. return validation.HasItemReferenceForAllCallIDs
  144. }
  145. func legacyHasFunctionCallOutput(reqBody map[string]any) bool {
  146. if reqBody == nil {
  147. return false
  148. }
  149. input, ok := reqBody["input"].([]any)
  150. if !ok {
  151. return false
  152. }
  153. for _, item := range input {
  154. itemMap, ok := item.(map[string]any)
  155. if !ok {
  156. continue
  157. }
  158. itemType, _ := itemMap["type"].(string)
  159. if itemType == "function_call_output" {
  160. return true
  161. }
  162. }
  163. return false
  164. }
  165. func legacyHasToolCallContext(reqBody map[string]any) bool {
  166. if reqBody == nil {
  167. return false
  168. }
  169. input, ok := reqBody["input"].([]any)
  170. if !ok {
  171. return false
  172. }
  173. for _, item := range input {
  174. itemMap, ok := item.(map[string]any)
  175. if !ok {
  176. continue
  177. }
  178. itemType, _ := itemMap["type"].(string)
  179. if itemType != "tool_call" && itemType != "function_call" {
  180. continue
  181. }
  182. if callID, ok := itemMap["call_id"].(string); ok && strings.TrimSpace(callID) != "" {
  183. return true
  184. }
  185. }
  186. return false
  187. }
  188. func legacyFunctionCallOutputCallIDs(reqBody map[string]any) []string {
  189. if reqBody == nil {
  190. return nil
  191. }
  192. input, ok := reqBody["input"].([]any)
  193. if !ok {
  194. return nil
  195. }
  196. ids := make(map[string]struct{})
  197. for _, item := range input {
  198. itemMap, ok := item.(map[string]any)
  199. if !ok {
  200. continue
  201. }
  202. itemType, _ := itemMap["type"].(string)
  203. if itemType != "function_call_output" {
  204. continue
  205. }
  206. if callID, ok := itemMap["call_id"].(string); ok && strings.TrimSpace(callID) != "" {
  207. ids[callID] = struct{}{}
  208. }
  209. }
  210. if len(ids) == 0 {
  211. return nil
  212. }
  213. callIDs := make([]string, 0, len(ids))
  214. for id := range ids {
  215. callIDs = append(callIDs, id)
  216. }
  217. return callIDs
  218. }
  219. func legacyHasFunctionCallOutputMissingCallID(reqBody map[string]any) bool {
  220. if reqBody == nil {
  221. return false
  222. }
  223. input, ok := reqBody["input"].([]any)
  224. if !ok {
  225. return false
  226. }
  227. for _, item := range input {
  228. itemMap, ok := item.(map[string]any)
  229. if !ok {
  230. continue
  231. }
  232. itemType, _ := itemMap["type"].(string)
  233. if itemType != "function_call_output" {
  234. continue
  235. }
  236. callID, _ := itemMap["call_id"].(string)
  237. if strings.TrimSpace(callID) == "" {
  238. return true
  239. }
  240. }
  241. return false
  242. }
  243. func legacyHasItemReferenceForCallIDs(reqBody map[string]any, callIDs []string) bool {
  244. if reqBody == nil || len(callIDs) == 0 {
  245. return false
  246. }
  247. input, ok := reqBody["input"].([]any)
  248. if !ok {
  249. return false
  250. }
  251. referenceIDs := make(map[string]struct{})
  252. for _, item := range input {
  253. itemMap, ok := item.(map[string]any)
  254. if !ok {
  255. continue
  256. }
  257. itemType, _ := itemMap["type"].(string)
  258. if itemType != "item_reference" {
  259. continue
  260. }
  261. idValue, _ := itemMap["id"].(string)
  262. idValue = strings.TrimSpace(idValue)
  263. if idValue == "" {
  264. continue
  265. }
  266. referenceIDs[idValue] = struct{}{}
  267. }
  268. if len(referenceIDs) == 0 {
  269. return false
  270. }
  271. for _, callID := range callIDs {
  272. if _, ok := referenceIDs[callID]; !ok {
  273. return false
  274. }
  275. }
  276. return true
  277. }
  278. func legacyParseWSIngressPayload(raw []byte) (eventType, model, promptCacheKey, previousResponseID string, payload map[string]any, err error) {
  279. values := gjson.GetManyBytes(raw, "type", "model", "prompt_cache_key", "previous_response_id")
  280. eventType = strings.TrimSpace(values[0].String())
  281. if eventType == "" {
  282. eventType = "response.create"
  283. }
  284. model = strings.TrimSpace(values[1].String())
  285. promptCacheKey = strings.TrimSpace(values[2].String())
  286. previousResponseID = strings.TrimSpace(values[3].String())
  287. payload = make(map[string]any)
  288. if err = json.Unmarshal(raw, &payload); err != nil {
  289. return "", "", "", "", nil, err
  290. }
  291. if _, exists := payload["type"]; !exists {
  292. payload["type"] = "response.create"
  293. }
  294. return eventType, model, promptCacheKey, previousResponseID, payload, nil
  295. }
  296. func optimizedParseWSIngressPayload(raw []byte) (eventType, model, promptCacheKey, previousResponseID string, payload map[string]any, err error) {
  297. payload = make(map[string]any)
  298. if err = json.Unmarshal(raw, &payload); err != nil {
  299. return "", "", "", "", nil, err
  300. }
  301. eventType = openAIWSPayloadString(payload, "type")
  302. if eventType == "" {
  303. eventType = "response.create"
  304. payload["type"] = eventType
  305. }
  306. model = openAIWSPayloadString(payload, "model")
  307. promptCacheKey = openAIWSPayloadString(payload, "prompt_cache_key")
  308. previousResponseID = openAIWSPayloadString(payload, "previous_response_id")
  309. return eventType, model, promptCacheKey, previousResponseID, payload, nil
  310. }
  311. func legacyExtractOpenAIUsageFromJSONBytes(body []byte) (OpenAIUsage, bool) {
  312. var response struct {
  313. Usage struct {
  314. InputTokens int `json:"input_tokens"`
  315. OutputTokens int `json:"output_tokens"`
  316. InputTokenDetails struct {
  317. CachedTokens int `json:"cached_tokens"`
  318. } `json:"input_tokens_details"`
  319. } `json:"usage"`
  320. }
  321. if err := json.Unmarshal(body, &response); err != nil {
  322. return OpenAIUsage{}, false
  323. }
  324. return OpenAIUsage{
  325. InputTokens: response.Usage.InputTokens,
  326. OutputTokens: response.Usage.OutputTokens,
  327. CacheReadInputTokens: response.Usage.InputTokenDetails.CachedTokens,
  328. }, true
  329. }