Turn on Kong's own AI telemetry instead of improvising it
Four settings, all of them off, which is why the AI gateway produced
HTTP-level telemetry and nothing about the model.
logging.log_payloads the prompt and the completion, in the log
records and — since OTel instrumentation is on
here — in the spans too
logging.log_statistics model usage and token counts
prometheus.ai_metrics kong_ai_llm_* : requests, tokens by type, cost,
provider latency, per model and per consumer
input_cost / output_cost cost per 1M tokens, which is what makes
kong_ai_llm_cost_total mean anything
Also removes this morning's pre-function/post-function body capture from
this gateway. It was the wrong tool for the LLM route: Kong's own
implementation reassembles a streamed SSE answer, while accumulating raw
chunks in kong.ctx.shared does not. The five business domains keep theirs
— their MCP traffic is not streamed the same way.
The costs are a snapshot, checked against OpenRouter's price list for
google/gemini-3.7-flash on 2026-08-27: $0.375 in, $1.875 out per 1M
tokens. They are substitution variables rather than literals precisely
because a stale figure produces confidently wrong money rather than an
error.
Note what this does NOT fix: usage is missing on streamed responses
because the OpenAI-compatible API only reports it when the caller asks
for stream_options.include_usage, and that is LibreChat's request to
make, not Kong's. Expect token counts to stay partial until then.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+5
-77
@@ -19,6 +19,11 @@ metadata:
|
||||
namespace: ${NAMESPACE}
|
||||
plugin: prometheus
|
||||
config:
|
||||
# The reason this gateway's prometheus config differs from the shared
|
||||
# template: it fronts an LLM. ai_metrics adds kong_ai_llm_* — requests,
|
||||
# tokens by type, cost and provider latency, per model and per consumer.
|
||||
# Without it none of that reaches Prometheus, whatever ai-proxy logs.
|
||||
ai_metrics: true
|
||||
status_code_metrics: true
|
||||
latency_metrics: true
|
||||
bandwidth_metrics: true
|
||||
@@ -66,12 +71,6 @@ config:
|
||||
http_endpoint: ${AUDIT_LOG_ENDPOINT}
|
||||
method: POST
|
||||
content_type: application/json
|
||||
# Reads what the two capture plugins below stashed in kong.ctx.shared. Both
|
||||
# are nil -- and silently omitted from the record -- if those plugins are not
|
||||
# applied, so this degrades to metadata-only logging rather than breaking.
|
||||
custom_fields_by_lua:
|
||||
request_body: "return kong.ctx.shared.audit_request_body"
|
||||
response_body: "return kong.ctx.shared.audit_response_body"
|
||||
---
|
||||
# prometheus and opentelemetry are bound control-plane-wide, matching the
|
||||
# shared template: they describe the gateway itself, not one service.
|
||||
@@ -122,74 +121,3 @@ spec:
|
||||
group: configuration.konghq.com
|
||||
kind: KongService
|
||||
name: ${NAME}-llm-proxy
|
||||
---
|
||||
# dev only: captures the raw request/response bodies into kong.ctx.shared for
|
||||
# the http-log plugin above to read back in the log phase. Same mechanism the
|
||||
# five business domains use (<domain>-api/kong/mcp/audit-log-body-capture.yaml).
|
||||
# Without it this gateway logged metadata only, so the prompts and completions
|
||||
# going through the LLM were the one thing the audit trail did not record.
|
||||
#
|
||||
# The response accumulator matters more here than elsewhere: LLM answers stream
|
||||
# as SSE, so body_filter runs once per chunk and the full completion only exists
|
||||
# after the last one.
|
||||
apiVersion: configuration.konghq.com/v1
|
||||
kind: KongPlugin
|
||||
metadata:
|
||||
name: ${NAME}-llm-audit-capture-request
|
||||
namespace: ${NAMESPACE}
|
||||
plugin: pre-function
|
||||
config:
|
||||
access:
|
||||
- "kong.ctx.shared.audit_request_body = kong.request.get_raw_body()"
|
||||
---
|
||||
apiVersion: configuration.konghq.com/v1
|
||||
kind: KongPlugin
|
||||
metadata:
|
||||
name: ${NAME}-llm-audit-capture-response
|
||||
namespace: ${NAMESPACE}
|
||||
plugin: post-function
|
||||
config:
|
||||
body_filter:
|
||||
- |
|
||||
local chunk = ngx.arg[1]
|
||||
if chunk then
|
||||
kong.ctx.shared.audit_response_body = (kong.ctx.shared.audit_response_body or "") .. chunk
|
||||
end
|
||||
---
|
||||
# Bound to the proxy service, like the audit log itself, so only real gateway
|
||||
# traffic is captured.
|
||||
apiVersion: configuration.konghq.com/v1alpha1
|
||||
kind: KongPluginBinding
|
||||
metadata:
|
||||
name: ${NAME}-llm-audit-capture-request-binding
|
||||
namespace: ${NAMESPACE}
|
||||
spec:
|
||||
controlPlaneRef:
|
||||
type: konnectNamespacedRef
|
||||
konnectNamespacedRef:
|
||||
name: ${CONTROL_PLANE_REF_NAME}
|
||||
pluginRef:
|
||||
name: ${NAME}-llm-audit-capture-request
|
||||
targets:
|
||||
serviceRef:
|
||||
group: configuration.konghq.com
|
||||
kind: KongService
|
||||
name: ${NAME}-llm-proxy
|
||||
---
|
||||
apiVersion: configuration.konghq.com/v1alpha1
|
||||
kind: KongPluginBinding
|
||||
metadata:
|
||||
name: ${NAME}-llm-audit-capture-response-binding
|
||||
namespace: ${NAMESPACE}
|
||||
spec:
|
||||
controlPlaneRef:
|
||||
type: konnectNamespacedRef
|
||||
konnectNamespacedRef:
|
||||
name: ${CONTROL_PLANE_REF_NAME}
|
||||
pluginRef:
|
||||
name: ${NAME}-llm-audit-capture-response
|
||||
targets:
|
||||
serviceRef:
|
||||
group: configuration.konghq.com
|
||||
kind: KongService
|
||||
name: ${NAME}-llm-proxy
|
||||
|
||||
Reference in New Issue
Block a user