This is an automated email from the ASF dual-hosted git repository.

nic443 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 ddddeafc2 feat: add core.response.get_response_source() API for 
response origin classification (#13224)
ddddeafc2 is described below

commit ddddeafc21d082f2c7090819d30b445c1122bd77
Author: Nic <[email protected]>
AuthorDate: Thu Apr 16 10:14:11 2026 +0800

    feat: add core.response.get_response_source() API for response origin 
classification (#13224)
---
 apisix/core/response.lua                  |  75 ++++++
 apisix/init.lua                           |   8 +
 apisix/plugins/ai-proxy/base.lua          |   3 +
 apisix/plugins/opentelemetry.lua          |  14 +-
 apisix/plugins/prometheus/exporter.lua    |   4 +
 apisix/plugins/zipkin.lua                 |   6 +-
 t/core/response-source.t                  | 389 ++++++++++++++++++++++++++++++
 t/plugin/opentelemetry4-bugfix-pb-state.t |   1 +
 t/plugin/prometheus.t                     |   2 +-
 t/plugin/prometheus2.t                    |   8 +-
 t/plugin/prometheus3.t                    |   2 +-
 t/plugin/prometheus4.t                    |   2 +-
 12 files changed, 500 insertions(+), 14 deletions(-)

diff --git a/apisix/core/response.lua b/apisix/core/response.lua
index ffc692eb8..55135fd5b 100644
--- a/apisix/core/response.lua
+++ b/apisix/core/response.lua
@@ -40,6 +40,7 @@ local str_sub = string.sub
 local tonumber = tonumber
 local clear_tab = require("table.clear")
 local pairs = pairs
+local ngx_var = ngx.var
 
 local _M = {version = 0.1}
 
@@ -87,6 +88,10 @@ function resp_exit(code, ...)
     end
 
     if code then
+        local ctx = ngx.ctx.api_ctx
+        if ctx and not ctx._resp_source then
+            ctx._resp_source = "apisix"
+        end
         if code >= 400 then
             tracer.finish_all(ngx.ctx, tracer.status.ERROR, "response code " 
.. code)
         end
@@ -159,6 +164,76 @@ function _M.get_upstream_status(ctx)
 end
 
 
+--- Explicitly set the response source for this request.
+-- Use this in plugins that bypass NGINX proxy (e.g. ai-proxy) to indicate
+-- whether the response originated from the upstream service.
+-- Must be called before core.response.exit() since exit() won't override
+-- an already-set source.
+function _M.set_response_source(ctx, source)
+    if ctx then
+        ctx._resp_source = source
+    end
+end
+
+
+--- Extract the last non-comma token from a comma/space-separated NGINX
+-- upstream variable string (e.g. "-, 0.002" → "0.002", "0, 0" → "0").
+-- Exported for testability; not part of the public API.
+function _M.get_last_upstream_token(s)
+    if not s then
+        return nil
+    end
+    local last
+    for token in s:gmatch("[^%s,]+") do
+        last = token
+    end
+    return last
+end
+
+
+--- Get the source of the current response.
+--
+-- @function core.response.get_response_source
+-- @tparam table ctx     The APISIX request context (api_ctx).
+-- @treturn string One of:
+--   "apisix"   — response generated by APISIX Lua code (e.g. route not found, 
plugin rejection)
+--   "nginx"    — error generated by NGINX proxy module (e.g. connection 
refused, timeout)
+--   "upstream" — real HTTP response returned by the upstream service
+function _M.get_response_source(ctx)
+    if not ctx then
+        return "apisix"
+    end
+
+    -- Priority 1: explicitly marked by core.response.exit() or 
set_response_source()
+    if ctx._resp_source then
+        return ctx._resp_source
+    end
+
+    -- Priority 2: request was proxied — inspect $upstream_header_time to
+    -- determine if the upstream actually sent response headers.
+    --
+    -- Use ngx.var directly (not ctx.var) because lua-var-nginx-module's FFI
+    -- path clamps header_time from -1 to 0 via ngx_max(ms, 0), losing the
+    -- "-" sentinel that NGINX uses to indicate "no response headers received"
+    -- (e.g. connection refused, connect timeout).  ngx.var preserves "-".
+    if ctx._apisix_proxied then
+        local header_time = ngx_var.upstream_header_time
+        if header_time then
+            local last = _M.get_last_upstream_token(header_time)
+            if last and last ~= "-" then
+                ctx._resp_source = "upstream"
+                return "upstream"
+            end
+        end
+        ctx._resp_source = "nginx"
+        return "nginx"
+    end
+
+    -- Fallback: never reached proxy_pass
+    return "apisix"
+end
+
+
 function _M.clear_header_as_body_modified()
     ngx.header.content_length = nil
     -- in case of upstream content is compressed content
diff --git a/apisix/init.lua b/apisix/init.lua
index b8ce4a11d..639709cd3 100644
--- a/apisix/init.lua
+++ b/apisix/init.lua
@@ -589,6 +589,14 @@ function _M.handle_upstream(api_ctx, route, 
enable_websocket)
     -- run the before_proxy method in access phase first to avoid always 
reinit request
     common_phase("before_proxy")
 
+    -- Mark that the request will be proxied to upstream via NGINX.
+    -- Must be set after before_proxy plugins (which may call 
core.response.exit())
+    -- and before proxy_pass dispatch, so the log phase can distinguish
+    -- NGINX proxy errors from upstream responses.
+    if not api_ctx._resp_source then
+        api_ctx._apisix_proxied = true
+    end
+
     local up_scheme = api_ctx.upstream_scheme
     if up_scheme == "grpcs" or up_scheme == "grpc" then
         stash_ngx_ctx()
diff --git a/apisix/plugins/ai-proxy/base.lua b/apisix/plugins/ai-proxy/base.lua
index 3cf054e45..c893c4ec4 100644
--- a/apisix/plugins/ai-proxy/base.lua
+++ b/apisix/plugins/ai-proxy/base.lua
@@ -203,6 +203,9 @@ function _M.before_proxy(conf, ctx, on_error)
                 return transport_http.handle_error(transport_err)
             end
 
+            -- Upstream responded — mark source before any early returns
+            core.response.set_response_source(ctx, "upstream")
+
             if res.status == 429 or (res.status >= 500 and res.status < 600) 
then
                 return res.status
             end
diff --git a/apisix/plugins/opentelemetry.lua b/apisix/plugins/opentelemetry.lua
index bf366936d..9185d7b20 100644
--- a/apisix/plugins/opentelemetry.lua
+++ b/apisix/plugins/opentelemetry.lua
@@ -461,19 +461,23 @@ end
 function _M.log(conf, api_ctx)
     if api_ctx.otel_context_token then
         -- ctx:detach() is not necessary, because of ctx is stored in ngx.ctx
-        local upstream_status = core.response.get_upstream_status(api_ctx)
+        local resp_source = core.response.get_response_source(api_ctx)
+        local status_code = ngx.status
 
         -- get span from current context
         local ctx = context:current()
         local span = ctx:span()
-        if upstream_status and upstream_status >= 500 then
+
+        span:set_attributes(attr.string("apisix.response_source", resp_source))
+
+        if status_code and status_code >= 500 then
             span:set_status(span_status.ERROR,
-                    "upstream response status: " .. upstream_status)
+                    resp_source .. " error: " .. status_code)
         end
 
         inject_core_spans(ctx, api_ctx, conf)
-        span:set_attributes(attr.int("http.status_code", upstream_status),
-                            attr.int("http.response.status_code", 
upstream_status))
+        span:set_attributes(attr.int("http.status_code", status_code),
+                            attr.int("http.response.status_code", status_code))
         update_time()
         span:finish()
     end
diff --git a/apisix/plugins/prometheus/exporter.lua 
b/apisix/plugins/prometheus/exporter.lua
index 2d4ed346e..ce89ca033 100644
--- a/apisix/plugins/prometheus/exporter.lua
+++ b/apisix/plugins/prometheus/exporter.lua
@@ -203,6 +203,7 @@ function _M.http_init(prometheus_enabled_in_stream)
             "HTTP status codes per service in APISIX",
             {"code", "route", "matched_uri", "matched_host", "service", 
"consumer", "node",
             "request_type", "request_llm_model", "llm_model",
+            "response_source",
             unpack(extra_labels("http_status"))},
             status_metrics_exptime)
 
@@ -317,10 +318,13 @@ function _M.http_log(conf, ctx)
         matched_host = ctx.curr_req_matched._host or ""
     end
 
+    local response_source = core.response.get_response_source(ctx)
+
     metrics.status:inc(1,
         gen_arr(vars.status, route_id, matched_uri, matched_host,
                 service_id, consumer_name, balancer_ip,
                 vars.request_type, vars.request_llm_model, vars.llm_model,
+                response_source,
                 unpack(extra_labels("http_status", ctx))))
 
     local latency, upstream_latency, apisix_latency = latency_details(ctx)
diff --git a/apisix/plugins/zipkin.lua b/apisix/plugins/zipkin.lua
index 285e0a45d..615828bc5 100644
--- a/apisix/plugins/zipkin.lua
+++ b/apisix/plugins/zipkin.lua
@@ -309,8 +309,10 @@ function _M.log(conf, ctx)
         opentracing.response_span:finish(log_end_time)
     end
 
-    local upstream_status = core.response.get_upstream_status(ctx)
-    opentracing.request_span:set_tag("http.status_code", upstream_status)
+    local resp_source = core.response.get_response_source(ctx)
+    local status_code = ngx.status
+    opentracing.request_span:set_tag("http.status_code", status_code)
+    opentracing.request_span:set_tag("apisix.response_source", resp_source)
 
     opentracing.request_span:finish(log_end_time)
 end
diff --git a/t/core/response-source.t b/t/core/response-source.t
new file mode 100644
index 000000000..4559595a4
--- /dev/null
+++ b/t/core/response-source.t
@@ -0,0 +1,389 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+use t::APISIX 'no_plan';
+
+repeat_each(1);
+no_long_string();
+no_shuffle();
+no_root_location();
+log_level("info");
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    if (!$block->request) {
+        $block->set_value("request", "GET /t");
+    }
+});
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: get_response_source returns "apisix" when ctx is nil
+--- config
+    location = /t {
+        content_by_lua_block {
+            local core = require("apisix.core")
+            local source = core.response.get_response_source(nil)
+            ngx.say(source)
+        }
+    }
+--- response_body
+apisix
+
+
+
+=== TEST 2: get_response_source returns "apisix" when no flags set
+--- config
+    location = /t {
+        content_by_lua_block {
+            local core = require("apisix.core")
+            local ctx = {}
+            local source = core.response.get_response_source(ctx)
+            ngx.say(source)
+        }
+    }
+--- response_body
+apisix
+
+
+
+=== TEST 3: get_response_source returns explicit _resp_source
+--- config
+    location = /t {
+        content_by_lua_block {
+            local core = require("apisix.core")
+            local ctx = {_resp_source = "apisix"}
+            ngx.say(core.response.get_response_source(ctx))
+            ctx._resp_source = "upstream"
+            ngx.say(core.response.get_response_source(ctx))
+            ctx._resp_source = "nginx"
+            ngx.say(core.response.get_response_source(ctx))
+        }
+    }
+--- response_body
+apisix
+upstream
+nginx
+
+
+
+=== TEST 4: get_last_upstream_token: nil input
+--- config
+    location = /t {
+        content_by_lua_block {
+            local core = require("apisix.core")
+            ngx.say(core.response.get_last_upstream_token(nil) or "nil")
+        }
+    }
+--- response_body
+nil
+
+
+
+=== TEST 5: get_last_upstream_token: single value
+--- config
+    location = /t {
+        content_by_lua_block {
+            local core = require("apisix.core")
+            ngx.say(core.response.get_last_upstream_token("0.002"))
+            ngx.say(core.response.get_last_upstream_token("-"))
+            ngx.say(core.response.get_last_upstream_token("0"))
+        }
+    }
+--- response_body
+0.002
+-
+0
+
+
+
+=== TEST 6: get_last_upstream_token: comma-separated retries
+--- config
+    location = /t {
+        content_by_lua_block {
+            local core = require("apisix.core")
+            -- first attempt failed, second succeeded
+            ngx.say(core.response.get_last_upstream_token("-, 0.002"))
+            -- both attempts failed
+            ngx.say(core.response.get_last_upstream_token("-, -"))
+            -- first succeeded, retry failed
+            ngx.say(core.response.get_last_upstream_token("0.002, -"))
+        }
+    }
+--- response_body
+0.002
+-
+-
+
+
+
+=== TEST 7: get_last_upstream_token: spaces around separators
+--- config
+    location = /t {
+        content_by_lua_block {
+            local core = require("apisix.core")
+            ngx.say(core.response.get_last_upstream_token("- , 0.001"))
+            ngx.say(core.response.get_last_upstream_token("0.001 , -"))
+            ngx.say(core.response.get_last_upstream_token("- , -"))
+        }
+    }
+--- response_body
+0.001
+-
+-
+
+
+
+=== TEST 8: get_last_upstream_token: colon-separated (upstream groups)
+--- config
+    location = /t {
+        content_by_lua_block {
+            local core = require("apisix.core")
+            -- colon separates upstream groups per NGINX docs
+            ngx.say(core.response.get_last_upstream_token("- : 0.003"))
+            ngx.say(core.response.get_last_upstream_token("0.003 : -"))
+        }
+    }
+--- response_body
+0.003
+-
+
+
+
+=== TEST 9: get_last_upstream_token: empty string
+--- config
+    location = /t {
+        content_by_lua_block {
+            local core = require("apisix.core")
+            ngx.say(core.response.get_last_upstream_token("") or "nil")
+        }
+    }
+--- response_body
+nil
+
+
+
+=== TEST 10: get_last_upstream_token: three retries
+--- config
+    location = /t {
+        content_by_lua_block {
+            local core = require("apisix.core")
+            ngx.say(core.response.get_last_upstream_token("-, -, 0.005"))
+            ngx.say(core.response.get_last_upstream_token("-, -, -"))
+        }
+    }
+--- response_body
+0.005
+-
+
+
+
+=== TEST 11: _resp_source takes priority over _apisix_proxied
+--- config
+    location = /t {
+        content_by_lua_block {
+            local core = require("apisix.core")
+            local ctx = {
+                _resp_source = "apisix",
+                _apisix_proxied = true,
+            }
+            local source = core.response.get_response_source(ctx)
+            ngx.say(source)
+        }
+    }
+--- response_body
+apisix
+
+
+
+=== TEST 12: set_response_source sets ctx._resp_source
+--- config
+    location = /t {
+        content_by_lua_block {
+            local core = require("apisix.core")
+            local ctx = {}
+            core.response.set_response_source(ctx, "upstream")
+            ngx.say(ctx._resp_source)
+            ngx.say(core.response.get_response_source(ctx))
+        }
+    }
+--- response_body
+upstream
+upstream
+
+
+
+=== TEST 13: resp_exit sets _resp_source = "apisix" for error codes
+--- config
+    location = /t {
+        access_by_lua_block {
+            ngx.ctx.api_ctx = {}
+            local core = require("apisix.core")
+            core.response.exit(403, "forbidden\n")
+        }
+        log_by_lua_block {
+            local ctx = ngx.ctx.api_ctx
+            ngx.log(ngx.INFO, "resp_source: ", ctx._resp_source or "nil")
+        }
+    }
+--- error_code: 403
+--- response_body
+forbidden
+--- error_log
+resp_source: apisix
+
+
+
+=== TEST 14: resp_exit sets _resp_source for success codes too
+--- config
+    location = /t {
+        access_by_lua_block {
+            ngx.ctx.api_ctx = {}
+            local core = require("apisix.core")
+            core.response.exit(200, "ok\n")
+        }
+        log_by_lua_block {
+            local ctx = ngx.ctx.api_ctx
+            ngx.log(ngx.INFO, "resp_source: ", ctx._resp_source or "nil")
+        }
+    }
+--- response_body
+ok
+--- error_log
+resp_source: apisix
+
+
+
+=== TEST 15: resp_exit does not override explicit set_response_source
+--- config
+    location = /t {
+        access_by_lua_block {
+            local ctx = {}
+            ngx.ctx.api_ctx = ctx
+            local core = require("apisix.core")
+            core.response.set_response_source(ctx, "upstream")
+            core.response.exit(200, "ok\n")
+        }
+        log_by_lua_block {
+            local ctx = ngx.ctx.api_ctx
+            ngx.log(ngx.INFO, "resp_source: ", ctx._resp_source or "nil")
+        }
+    }
+--- response_body
+ok
+--- error_log
+resp_source: upstream
+
+
+
+=== TEST 16: integration - upstream returns 200, response_source = "upstream"
+--- apisix_yaml
+routes:
+    -
+        uri: /hello
+        plugins:
+            serverless-pre-function:
+                phase: log
+                functions:
+                    - "return function(_, ctx) ngx.log(ngx.WARN, 'resp_source: 
', require('apisix.core').response.get_response_source(ctx)) end"
+        upstream:
+            nodes:
+                "127.0.0.1:1980": 1
+            type: roundrobin
+#END
+--- request
+GET /hello
+--- error_code: 200
+--- error_log
+resp_source: upstream
+
+
+
+=== TEST 17: integration - upstream connection refused, response_source = 
"nginx"
+--- apisix_yaml
+routes:
+    -
+        uri: /hello
+        plugins:
+            serverless-pre-function:
+                phase: log
+                functions:
+                    - "return function(_, ctx) ngx.log(ngx.WARN, 'resp_source: 
', require('apisix.core').response.get_response_source(ctx)) end"
+        upstream:
+            nodes:
+                "127.0.0.1:11111": 1
+            type: roundrobin
+#END
+--- request
+GET /hello
+--- error_code: 502
+--- error_log
+resp_source: nginx
+
+
+
+=== TEST 18: integration - upstream returns 502, response_source = "upstream"
+This verifies that a real 502 from upstream is classified as "upstream", not 
"nginx".
+--- apisix_yaml
+routes:
+    -
+        uri: /specific_status
+        plugins:
+            serverless-pre-function:
+                phase: log
+                functions:
+                    - "return function(_, ctx) ngx.log(ngx.WARN, 'resp_source: 
', require('apisix.core').response.get_response_source(ctx)) end"
+        upstream:
+            nodes:
+                "127.0.0.1:1980": 1
+            type: roundrobin
+#END
+--- request
+GET /specific_status
+--- more_headers
+X-Test-Upstream-Status: 502
+--- error_code: 502
+--- error_log
+resp_source: upstream
+
+
+
+=== TEST 19: integration - APISIX plugin rejects request, response_source = 
"apisix"
+--- apisix_yaml
+routes:
+    -
+        uri: /hello
+        plugins:
+            serverless-pre-function:
+                functions:
+                    - "return function() local core = require('apisix.core'); 
core.response.exit(403, 'rejected by plugin') end"
+            serverless-post-function:
+                phase: log
+                functions:
+                    - "return function(_, ctx) ngx.log(ngx.WARN, 'resp_source: 
', require('apisix.core').response.get_response_source(ctx)) end"
+        upstream:
+            nodes:
+                "127.0.0.1:1980": 1
+            type: roundrobin
+#END
+--- request
+GET /hello
+--- error_code: 403
+--- error_log
+resp_source: apisix
diff --git a/t/plugin/opentelemetry4-bugfix-pb-state.t 
b/t/plugin/opentelemetry4-bugfix-pb-state.t
index bc8405df7..f522f2503 100644
--- a/t/plugin/opentelemetry4-bugfix-pb-state.t
+++ b/t/plugin/opentelemetry4-bugfix-pb-state.t
@@ -189,6 +189,7 @@ type 'opentelemetry.proto.trace.v1.TracesData' does not 
exists
 --- grep_error_log eval
 qr/attribute (apisix|x-my).+?:.[^,]*/
 --- grep_error_log_out
+attribute apisix.response_source: "upstream"
 attribute apisix.route_id: "1"
 attribute apisix.route_name: "route_name"
 attribute x-my-header-name: "william"
diff --git a/t/plugin/prometheus.t b/t/plugin/prometheus.t
index d29406886..8edfd5299 100644
--- a/t/plugin/prometheus.t
+++ b/t/plugin/prometheus.t
@@ -394,7 +394,7 @@ passed
 --- request
 GET /apisix/prometheus/metrics
 --- response_body eval
-qr/apisix_http_status\{code="404",route="3",matched_uri="\/hello3",matched_host="",service="",consumer="",node="127.0.0.1",request_type="traditional_http",request_llm_model="",llm_model=""\}
 2/
+qr/apisix_http_status\{code="404",route="3",matched_uri="\/hello3",matched_host="",service="",consumer="",node="127.0.0.1",request_type="traditional_http",request_llm_model="",llm_model="",response_source="[^"]*"\}
 2/
 
 
 
diff --git a/t/plugin/prometheus2.t b/t/plugin/prometheus2.t
index 9c19f0da2..cf303ce77 100644
--- a/t/plugin/prometheus2.t
+++ b/t/plugin/prometheus2.t
@@ -180,7 +180,7 @@ apikey: auth-one
 --- request
 GET /apisix/prometheus/metrics
 --- response_body eval
-qr/apisix_http_status\{code="200",route="1",matched_uri="\/hello",matched_host="",service="",consumer="jack",node="127.0.0.1",request_type="traditional_http",request_llm_model="",llm_model=""\}
 \d+/
+qr/apisix_http_status\{code="200",route="1",matched_uri="\/hello",matched_host="",service="",consumer="jack",node="127.0.0.1",request_type="traditional_http",request_llm_model="",llm_model="",response_source="[^"]*"\}
 \d+/
 
 
 
@@ -256,7 +256,7 @@ GET /not_found
 --- request
 GET /apisix/prometheus/metrics
 --- response_body eval
-qr/apisix_http_status\{code="404",route="",matched_uri="",matched_host="",service="",consumer="",node="",request_type="traditional_http",request_llm_model="",llm_model=""\}
 \d+/
+qr/apisix_http_status\{code="404",route="",matched_uri="",matched_host="",service="",consumer="",node="",request_type="traditional_http",request_llm_model="",llm_model="",response_source="[^"]*"\}
 \d+/
 
 
 
@@ -275,7 +275,7 @@ qr/404 Not Found/
 --- request
 GET /apisix/prometheus/metrics
 --- response_body eval
-qr/apisix_http_status\{code="404",route="9",matched_uri="\/foo\*",matched_host="foo.com",service="",consumer="",node="127.0.0.1",request_type="traditional_http",request_llm_model="",llm_model=""\}
 \d+/
+qr/apisix_http_status\{code="404",route="9",matched_uri="\/foo\*",matched_host="foo.com",service="",consumer="",node="127.0.0.1",request_type="traditional_http",request_llm_model="",llm_model="",response_source="[^"]*"\}
 \d+/
 
 
 
@@ -294,7 +294,7 @@ qr/404 Not Found/
 --- request
 GET /apisix/prometheus/metrics
 --- response_body eval
-qr/apisix_http_status\{code="404",route="9",matched_uri="\/bar\*",matched_host="bar.com",service="",consumer="",node="127.0.0.1",request_type="traditional_http",request_llm_model="",llm_model=""\}
 \d+/
+qr/apisix_http_status\{code="404",route="9",matched_uri="\/bar\*",matched_host="bar.com",service="",consumer="",node="127.0.0.1",request_type="traditional_http",request_llm_model="",llm_model="",response_source="[^"]*"\}
 \d+/
 
 
 
diff --git a/t/plugin/prometheus3.t b/t/plugin/prometheus3.t
index 0440b1e84..57c2f73df 100644
--- a/t/plugin/prometheus3.t
+++ b/t/plugin/prometheus3.t
@@ -270,4 +270,4 @@ opentracing
 --- request
 GET /apisix/prometheus/metrics
 --- response_body eval
-qr/apisix_http_status\{code="200",route="1",matched_uri="\/opentracing",matched_host="",service="",consumer="",node="127.0.0.1",request_type="traditional_http",request_llm_model="",llm_model=""\}
 1/
+qr/apisix_http_status\{code="200",route="1",matched_uri="\/opentracing",matched_host="",service="",consumer="",node="127.0.0.1",request_type="traditional_http",request_llm_model="",llm_model="",response_source="[^"]*"\}
 1/
diff --git a/t/plugin/prometheus4.t b/t/plugin/prometheus4.t
index d30946a7b..5e2fa8b86 100644
--- a/t/plugin/prometheus4.t
+++ b/t/plugin/prometheus4.t
@@ -143,7 +143,7 @@ GET /hello
 --- request
 GET /apisix/prometheus/metrics
 --- response_body eval
-qr/apisix_http_status\{code="200",route="10",matched_uri="\/hello",matched_host="",service="",consumer="",node="127.0.0.1",request_type="traditional_http",request_llm_model="",llm_model="",dummy=""\}
 \d+/
+qr/apisix_http_status\{code="200",route="10",matched_uri="\/hello",matched_host="",service="",consumer="",node="127.0.0.1",request_type="traditional_http",request_llm_model="",llm_model="",response_source="[^"]*",dummy=""\}
 \d+/
 
 
 

Reply via email to