This is an automated email from the ASF dual-hosted git repository.
membphis 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 df0a28425 feat(ai-proxy): include AI observability vars in llm_summary
df0a28425 is described below
commit df0a28425350541b3ac09c4403688d5f11b6ab8f
Author: Nic <[email protected]>
AuthorDate: Fri Jun 26 14:23:49 2026 +0800
feat(ai-proxy): include AI observability vars in llm_summary
---
apisix/plugins/ai-proxy/base.lua | 8 +++++
docs/en/latest/plugins/ai-proxy-multi.md | 13 ++++++-
docs/en/latest/plugins/ai-proxy.md | 13 ++++++-
docs/zh/latest/plugins/ai-proxy-multi.md | 13 ++++++-
docs/zh/latest/plugins/ai-proxy.md | 13 ++++++-
t/plugin/ai-proxy-kafka-log.t | 61 ++++++++++++++++++++++++++++++++
6 files changed, 117 insertions(+), 4 deletions(-)
diff --git a/apisix/plugins/ai-proxy/base.lua b/apisix/plugins/ai-proxy/base.lua
index cb258ff30..97c980dc5 100644
--- a/apisix/plugins/ai-proxy/base.lua
+++ b/apisix/plugins/ai-proxy/base.lua
@@ -80,6 +80,14 @@ function _M.set_logging(ctx, summaries, payloads)
completion_tokens = ctx.var.llm_completion_tokens,
total_tokens = ctx.var.llm_total_tokens,
upstream_response_time = ctx.var.apisix_upstream_response_time,
+ stream = ctx.var.llm_stream,
+ tool_count = ctx.var.llm_tool_count,
+ has_tool_calls = ctx.var.llm_has_tool_calls,
+ end_user_id = ctx.var.llm_end_user_id,
+ cache_read_input_tokens = ctx.var.llm_cache_read_input_tokens,
+ cache_creation_input_tokens =
ctx.var.llm_cache_creation_input_tokens,
+ reasoning_tokens = ctx.var.llm_reasoning_tokens,
+ content_risk_level = ctx.var.llm_content_risk_level,
}
end
if payloads then
diff --git a/docs/en/latest/plugins/ai-proxy-multi.md
b/docs/en/latest/plugins/ai-proxy-multi.md
index d95f9c26e..a91b0f43b 100644
--- a/docs/en/latest/plugins/ai-proxy-multi.md
+++ b/docs/en/latest/plugins/ai-proxy-multi.md
@@ -2605,7 +2605,18 @@ The following example demonstrates how you can log LLM
request related informati
* `llm_time_to_first_token`: Duration from request sending to the first token
received from the LLM service, in milliseconds.
* `llm_model`: LLM model.
* `llm_prompt_tokens`: Number of tokens in the prompt.
-* `llm_completion_tokens`: Number of chat completion tokens in the prompt.
+* `llm_completion_tokens`: Number of chat completion tokens in the response.
+* `llm_total_tokens`: Total number of tokens used (prompt plus completion).
+* `llm_cache_read_input_tokens`: Number of input tokens read from cache.
+* `llm_cache_creation_input_tokens`: Number of input tokens written to cache.
+* `llm_reasoning_tokens`: Number of reasoning tokens generated.
+* `llm_stream`: Whether the request is a streaming request (`true` or `false`).
+* `llm_tool_count`: Number of tools provided in the request.
+* `llm_has_tool_calls`: `true` when the response contains tool calls.
+* `llm_end_user_id`: End user identifier extracted from the request (e.g., the
OpenAI `user` field).
+* `llm_content_risk_level`: Content risk level reported by content moderation.
+
+When `logging.summaries` is enabled, these variables are also emitted in the
`llm_summary` log object (using the names without the `llm_` prefix), so logger
plugins can consume them without additional configuration.
Update the access log format in your configuration file to include additional
LLM related variables:
diff --git a/docs/en/latest/plugins/ai-proxy.md
b/docs/en/latest/plugins/ai-proxy.md
index e0d78e102..17992d03f 100644
--- a/docs/en/latest/plugins/ai-proxy.md
+++ b/docs/en/latest/plugins/ai-proxy.md
@@ -2081,7 +2081,18 @@ The following example demonstrates how you can log LLM
request related informati
* `llm_time_to_first_token`: Duration from request sending to the first token
received from the LLM service, in milliseconds.
* `llm_model`: LLM model.
* `llm_prompt_tokens`: Number of tokens in the prompt.
-* `llm_completion_tokens`: Number of chat completion tokens in the prompt.
+* `llm_completion_tokens`: Number of chat completion tokens in the response.
+* `llm_total_tokens`: Total number of tokens used (prompt plus completion).
+* `llm_cache_read_input_tokens`: Number of input tokens read from cache.
+* `llm_cache_creation_input_tokens`: Number of input tokens written to cache.
+* `llm_reasoning_tokens`: Number of reasoning tokens generated.
+* `llm_stream`: Whether the request is a streaming request (`true` or `false`).
+* `llm_tool_count`: Number of tools provided in the request.
+* `llm_has_tool_calls`: `true` when the response contains tool calls.
+* `llm_end_user_id`: End user identifier extracted from the request (e.g., the
OpenAI `user` field).
+* `llm_content_risk_level`: Content risk level reported by content moderation.
+
+When `logging.summaries` is enabled, these variables are also emitted in the
`llm_summary` log object (using the names without the `llm_` prefix), so logger
plugins can consume them without additional configuration.
In addition, the following standard nginx upstream variables are automatically
populated when `ai-proxy` sends requests via cosocket transport:
diff --git a/docs/zh/latest/plugins/ai-proxy-multi.md
b/docs/zh/latest/plugins/ai-proxy-multi.md
index 1a6bd5ee6..8f3ea99dc 100644
--- a/docs/zh/latest/plugins/ai-proxy-multi.md
+++ b/docs/zh/latest/plugins/ai-proxy-multi.md
@@ -2715,7 +2715,18 @@ curl "http://127.0.0.1:9080/anything" -X POST \
* `llm_time_to_first_token`:从发送请求到从 LLM 服务接收第一个令牌的持续时间(毫秒)。
* `llm_model`:LLM 模型。
* `llm_prompt_tokens`:提示中的令牌数量。
-* `llm_completion_tokens`:提示中的聊天完成令牌数量。
+* `llm_completion_tokens`:响应中的聊天完成令牌数量。
+* `llm_total_tokens`:使用的总令牌数(提示加完成)。
+* `llm_cache_read_input_tokens`:从缓存读取的输入令牌数量。
+* `llm_cache_creation_input_tokens`:写入缓存的输入令牌数量。
+* `llm_reasoning_tokens`:生成的推理令牌数量。
+* `llm_stream`:请求是否为流式请求(`true` 或 `false`)。
+* `llm_tool_count`:请求中提供的工具数量。
+* `llm_has_tool_calls`:当响应包含工具调用时为 `true`。
+* `llm_end_user_id`:从请求中提取的终端用户标识(例如 OpenAI 的 `user` 字段)。
+* `llm_content_risk_level`:内容审核报告的内容风险等级。
+
+当启用 `logging.summaries` 时,这些变量也会写入 `llm_summary` 日志对象(使用去掉 `llm_`
前缀的名称),日志插件无需额外配置即可使用。
在配置文件中更新访问日志格式以包含其他 LLM 相关变量:
diff --git a/docs/zh/latest/plugins/ai-proxy.md
b/docs/zh/latest/plugins/ai-proxy.md
index 2bbe940dd..aa5d0cce9 100644
--- a/docs/zh/latest/plugins/ai-proxy.md
+++ b/docs/zh/latest/plugins/ai-proxy.md
@@ -2081,7 +2081,18 @@ curl "http://127.0.0.1:9080/anything" -X POST \
* `llm_time_to_first_token`:从发送请求到从 LLM 服务接收第一个令牌的持续时间(毫秒)。
* `llm_model`:LLM 模型。
* `llm_prompt_tokens`:提示中的令牌数量。
-* `llm_completion_tokens`:提示中的聊天完成令牌数量。
+* `llm_completion_tokens`:响应中的聊天完成令牌数量。
+* `llm_total_tokens`:使用的总令牌数(提示加完成)。
+* `llm_cache_read_input_tokens`:从缓存读取的输入令牌数量。
+* `llm_cache_creation_input_tokens`:写入缓存的输入令牌数量。
+* `llm_reasoning_tokens`:生成的推理令牌数量。
+* `llm_stream`:请求是否为流式请求(`true` 或 `false`)。
+* `llm_tool_count`:请求中提供的工具数量。
+* `llm_has_tool_calls`:当响应包含工具调用时为 `true`。
+* `llm_end_user_id`:从请求中提取的终端用户标识(例如 OpenAI 的 `user` 字段)。
+* `llm_content_risk_level`:内容审核报告的内容风险等级。
+
+当启用 `logging.summaries` 时,这些变量也会写入 `llm_summary` 日志对象(使用去掉 `llm_`
前缀的名称),日志插件无需额外配置即可使用。
在配置文件中更新访问日志格式以包含其他 LLM 相关变量:
diff --git a/t/plugin/ai-proxy-kafka-log.t b/t/plugin/ai-proxy-kafka-log.t
index 757bbe178..a6fddc052 100644
--- a/t/plugin/ai-proxy-kafka-log.t
+++ b/t/plugin/ai-proxy-kafka-log.t
@@ -137,6 +137,10 @@ X-AI-Fixture: openai/chat-basic.json
send data to kafka:
llm_request
llm_summary
+tool_count
+cache_read_input_tokens
+cache_creation_input_tokens
+reasoning_tokens
You are a mathematician
gpt-35-turbo-instruct
llm_response_text
@@ -413,3 +417,60 @@ send data to kafka:
llm_request
llm_summary
some content
+
+
+
+=== TEST 9: set_logging records every observability field in llm_summary
+--- config
+ location /t {
+ content_by_lua_block {
+ local base = require("apisix.plugins.ai-proxy.base")
+ local ctx = {
+ var = {
+ request_llm_model = "m-req",
+ llm_model = "m",
+ llm_time_to_first_token = 12,
+ llm_prompt_tokens = 11,
+ llm_completion_tokens = 22,
+ llm_total_tokens = 33,
+ apisix_upstream_response_time = 1.5,
+ llm_stream = "true",
+ llm_tool_count = 2,
+ llm_has_tool_calls = "true",
+ llm_end_user_id = "user-1",
+ llm_cache_read_input_tokens = 5,
+ llm_cache_creation_input_tokens = 6,
+ llm_reasoning_tokens = 7,
+ llm_content_risk_level = "low",
+ }
+ }
+ base.set_logging(ctx, true, false)
+ local s = ctx.llm_summary
+ local keys = {
+ "request_model", "model", "duration", "prompt_tokens",
+ "completion_tokens", "total_tokens", "upstream_response_time",
+ "stream", "tool_count", "has_tool_calls", "end_user_id",
+ "cache_read_input_tokens", "cache_creation_input_tokens",
+ "reasoning_tokens", "content_risk_level",
+ }
+ for _, k in ipairs(keys) do
+ ngx.say(k, "=", tostring(s[k]))
+ end
+ }
+ }
+--- response_body
+request_model=m-req
+model=m
+duration=12
+prompt_tokens=11
+completion_tokens=22
+total_tokens=33
+upstream_response_time=1.5
+stream=true
+tool_count=2
+has_tool_calls=true
+end_user_id=user-1
+cache_read_input_tokens=5
+cache_creation_input_tokens=6
+reasoning_tokens=7
+content_risk_level=low