This is an automated email from the ASF dual-hosted git repository. ashishtiwari pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push: new de03766ea fix(ai-proxy): check type of choices/usage/content fields before use it (#12548) de03766ea is described below commit de03766eaef9e000d58baf7766cb55225b12c69f Author: Ashish Tiwari <ashishjaitiwari15112...@gmail.com> AuthorDate: Thu Aug 28 13:11:52 2025 +0530 fix(ai-proxy): check type of choices/usage/content fields before use it (#12548) --- apisix/plugins/ai-drivers/openai-base.lua | 21 +++++---- t/plugin/ai-proxy3.t | 71 +++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 9 deletions(-) diff --git a/apisix/plugins/ai-drivers/openai-base.lua b/apisix/plugins/ai-drivers/openai-base.lua index 4607cbca8..0b105d729 100644 --- a/apisix/plugins/ai-drivers/openai-base.lua +++ b/apisix/plugins/ai-drivers/openai-base.lua @@ -163,17 +163,20 @@ local function read_response(ctx, res) ", it will cause token usage not available") else core.log.info("got token usage from ai service: ", core.json.delay_encode(res_body.usage)) - ctx.ai_token_usage = { - prompt_tokens = res_body.usage and res_body.usage.prompt_tokens or 0, - completion_tokens = res_body.usage and res_body.usage.completion_tokens or 0, - total_tokens = res_body.usage and res_body.usage.total_tokens or 0, - } - ctx.var.llm_prompt_tokens = ctx.ai_token_usage.prompt_tokens - ctx.var.llm_completion_tokens = ctx.ai_token_usage.completion_tokens - if res_body.choices and #res_body.choices > 0 then + ctx.ai_token_usage = {} + if type(res_body.usage) == "table" then + ctx.ai_token_usage.prompt_tokens = res_body.usage.prompt_tokens or 0 + ctx.ai_token_usage.completion_tokens = res_body.usage.completion_tokens or 0 + ctx.ai_token_usage.total_tokens = res_body.usage.total_tokens or 0 + end + ctx.var.llm_prompt_tokens = ctx.ai_token_usage.prompt_tokens or 0 + ctx.var.llm_completion_tokens = ctx.ai_token_usage.completion_tokens or 0 + if type(res_body.choices) == "table" and #res_body.choices > 0 then local contents = {} for _, choice in ipairs(res_body.choices) do - if choice and choice.message and choice.message.content then + if type(choice) == "table" + and type(choice.message) == "table" + and type(choice.message.content) == "string" then core.table.insert(contents, choice.message.content) end end diff --git a/t/plugin/ai-proxy3.t b/t/plugin/ai-proxy3.t index 3e39f5d39..ec771f690 100644 --- a/t/plugin/ai-proxy3.t +++ b/t/plugin/ai-proxy3.t @@ -87,6 +87,30 @@ add_block_preprocessor(sub { "completion_tokens": 20, "total_tokens": 30 } +}]] + ngx.status = 200 + ngx.say(res) + } + } + + location /null-content { + content_by_lua_block { + local json = require("cjson.safe") + + local res = [[ +{ + "model": "gpt-3.5-turbo", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": null + }, + "finish_reason": "stop" + } + ], + "usage": null }]] ngx.status = 200 ngx.say(res) @@ -153,3 +177,50 @@ POST /anything qr/.*completion_tokens.*/ --- access_log eval qr/.*gpt-3.5-turbo \d+ 10 20.*/ + + + +=== TEST 3: proxy to /null-content ai endpoint +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/anything", + "plugins": { + "ai-proxy": { + "provider": "openai", + "auth": { + "header": { + "Authorization": "Bearer token" + } + }, + "override": { + "endpoint": "http://localhost:6724/null-content" + } + } + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 4: send request +--- request +POST /anything +{"messages":[{"role":"user","content":"What is 1+1?"}], "model": "gpt-4"} +--- error_code: 200 +--- response_body eval +qr/.*assistant.*/ +--- no_error_log