diff --git a/skills/a6-plugin-ai-content-moderation/SKILL.md b/skills/a6-plugin-ai-content-moderation/SKILL.md index 9b9f4bb..87a82e7 100644 --- a/skills/a6-plugin-ai-content-moderation/SKILL.md +++ b/skills/a6-plugin-ai-content-moderation/SKILL.md @@ -1,10 +1,9 @@ --- name: a6-plugin-ai-content-moderation description: >- - Skill for configuring APISIX AI content moderation plugins via the a6 CLI. - Covers both ai-aws-content-moderation (AWS Comprehend, request-only) and - ai-aliyun-content-moderation (Aliyun, request + response with streaming), - toxicity thresholds, category filtering, and integration with ai-proxy. + Skill for configuring APISIX AWS and Aliyun AI content moderation via the + a6 CLI. Covers request and response checks, streaming, deny_code, and + ai-proxy. version: "1.0.0" author: Apache APISIX Contributors license: Apache-2.0 @@ -29,15 +28,21 @@ in LLM requests and responses: | Plugin | Provider | Request | Response | Streaming | |--------|----------|---------|----------|-----------| -| `ai-aws-content-moderation` | AWS Comprehend | ✅ | ❌ | ❌ | +| `ai-aws-content-moderation` | AWS Comprehend | ✅ | ✅ | ✅ | | `ai-aliyun-content-moderation` | Aliyun Moderation Plus | ✅ | ✅ | ✅ | -Both must be used alongside `ai-proxy` or `ai-proxy-multi`. +Both must be used alongside `ai-proxy` or `ai-proxy-multi` so the plugin can +moderate decoded AI content. AWS response and streaming moderation, `fail_mode`, +role selection, and the default `deny_code` of `200` apply from APISIX 3.18.0. +For field tables and protocol details, see +https://docs.api7.ai/hub/ai-aws-content-moderation and +https://docs.api7.ai/hub/ai-aliyun-content-moderation. ## When to Use - Block toxic, hateful, or sexual content before it reaches the LLM -- Filter harmful LLM responses before they reach clients (Aliyun only) +- Moderate harmful LLM responses. Non-streaming responses can be denied before + delivery; streaming checks cannot retract chunks already sent - Enforce content policies with configurable thresholds - Comply with content safety regulations @@ -46,19 +51,21 @@ Both must be used alongside `ai-proxy` or `ai-proxy-multi`. ``` ai-prompt-template (priority 1071) ai-prompt-decorator (priority 1070) -ai-aws-content-moderation (priority 1050) ← runs BEFORE ai-proxy ai-proxy (priority 1040) +ai-aws-content-moderation (priority 1031) ← runs AFTER ai-proxy ai-aliyun-content-moderation (priority 1029) ← runs AFTER ai-proxy ``` -The AWS plugin blocks requests before they reach the LLM. The Aliyun plugin -runs after `ai-proxy` sets context and can check both requests and responses. +A larger priority number runs earlier. Both moderation plugins run after +`ai-proxy` or `ai-proxy-multi` so they can read the decoded AI request. +They still block a flagged request before the upstream LLM is called. --- ## Plugin 1: ai-aws-content-moderation -Uses the AWS Comprehend `detectToxicContent` API to score request content. +Uses the AWS Comprehend `DetectToxicContent` API to score request and response +content. ### Configuration Reference @@ -69,6 +76,18 @@ Uses the AWS Comprehend `detectToxicContent` API to score request content. | `comprehend.region` | string | **Yes** | — | AWS region (e.g. `us-east-1`) | | `comprehend.endpoint` | string | No | Auto | Custom Comprehend endpoint | | `comprehend.ssl_verify` | boolean | No | `true` | Verify SSL certificate | +| `check_request` | boolean | No | `true` | Enable request moderation | +| `check_response` | boolean | No | `false` | Enable response moderation | +| `request_check_roles` | array | No | `user`, `tool`, `system`, `assistant` | Roles to moderate on the request | +| `request_check_mode` | string | No | `all` | `all` or `last` (latest consecutive block). `system` is always checked when selected | +| `request_check_length_limit` | integer | No | `1000` | Maximum bytes per Comprehend request text segment | +| `response_check_length_limit` | integer | No | `1000` | Maximum bytes per Comprehend response text segment | +| `stream_check_mode` | string | No | `final_packet` | `realtime` or `final_packet` when `check_response` is true | +| `stream_check_cache_size` | integer | No | `128` | Maximum characters per moderation batch in `realtime` mode | +| `stream_check_interval` | number | No | `3` | Seconds between moderation batches in `realtime` mode | +| `fail_mode` | string | No | `skip` | `skip`, `warn`, or `error` for non-AI / unrecognized traffic | +| `deny_code` | number | No | `200` | HTTP status for a denied request before headers are sent | +| `deny_message` | string | No | — | Custom denial message | | `moderation_categories` | object | No | — | Per-category thresholds (0-1) | | `moderation_threshold` | number | No | `0.5` | Overall toxicity threshold (0-1) | @@ -125,7 +144,9 @@ a6 route create -f - <<'EOF' EOF ``` -Toxic requests are rejected with HTTP 400: +By default, a flagged request is denied with HTTP 200 and a provider-compatible +refusal so AI SDKs can parse the body. Set `deny_code: 400` when clients must +treat moderation as an HTTP error. ``` request body exceeds HATE_SPEECH threshold @@ -172,6 +193,9 @@ response moderation, and real-time streaming moderation. | `request_check_length_limit` | number | No | `2000` | Max chars per request chunk | | `response_check_service` | string | No | `llm_response_moderation` | Aliyun service for response checks | | `response_check_length_limit` | number | No | `5000` | Max chars per response chunk | +| `request_check_mode` | string | No | `last` | `last` (latest consecutive selected turns) or `all` | +| `request_check_roles` | array | No | `["user"]` | `user`, `tool`, or `system`. Assistant history cannot be selected | +| `fail_mode` | string | No | `skip` | `skip`, `warn`, or `error` for non-AI / unrecognized traffic | | `risk_level_bar` | string | No | `high` | Threshold: `none`, `low`, `medium`, `high`, `max` | | `deny_code` | number | No | `200` | HTTP status code for rejected content | | `deny_message` | string | No | — | Custom rejection message | @@ -193,8 +217,8 @@ Setting `risk_level_bar: "low"` blocks everything rated `low` or above. | Mode | Behavior | |------|----------| -| `final_packet` | Buffers entire response, checks at end | -| `realtime` | Checks content in batches during streaming, can interrupt mid-response | +| `final_packet` | Checks the assembled response at the end and annotates the final stream packet with `risk_level`; it cannot retract earlier chunks | +| `realtime` | Checks content in batches during streaming and can replace the remainder of the stream after a violation; it cannot retract earlier chunks | ### Step-by-Step: Aliyun Request + Response Moderation @@ -260,7 +284,7 @@ EOF ### Pattern A: Request-only filtering (AWS) ``` -Client → [AWS Comprehend blocks toxic] → ai-proxy → LLM → Response → Client +Client → ai-proxy [sets context] → [AWS Comprehend blocks toxic] → LLM → Response → Client ``` ```yaml @@ -347,9 +371,10 @@ routes: | Symptom | Cause | Fix | |---------|-------|-----| -| "no ai instance picked" | Aliyun plugin used without ai-proxy | Always configure ai-proxy or ai-proxy-multi on the same route | -| AWS plugin not blocking | Threshold too permissive | Lower `moderation_threshold` or per-category thresholds | +| "no ai instance picked" | Moderation plugin used without ai-proxy | Always configure ai-proxy or ai-proxy-multi on the same route | +| AWS denial is HTTP 200 | Default `deny_code` is `200` | Set `deny_code: 400` if clients expect an HTTP error | +| AWS plugin not blocking | Threshold too permissive, or `request_check_roles` omitted the role | Lower thresholds; AWS defaults to all roles with `request_check_mode: all` | | Aliyun response moderation inactive | `check_response` defaults to `false` | Explicitly set `check_response: true` | | "Specified signature is not matched" | Wrong Aliyun credentials | Verify `access_key_id` and `access_key_secret` | | High latency | Double moderation (both plugins) | Use one moderation provider per route, not both | -| Streaming interrupted mid-response | Aliyun realtime mode detected violation | Expected behavior; adjust `risk_level_bar` or use `final_packet` mode | +| Streaming interrupted mid-response | A moderation plugin in `realtime` mode detected a violation | Expected behavior; adjust the moderation threshold or use `final_packet` mode | diff --git a/skills/a6-plugin-limit-count/SKILL.md b/skills/a6-plugin-limit-count/SKILL.md index c947173..8b3e73a 100644 --- a/skills/a6-plugin-limit-count/SKILL.md +++ b/skills/a6-plugin-limit-count/SKILL.md @@ -1,11 +1,8 @@ --- name: a6-plugin-limit-count description: >- - Skill for configuring the Apache APISIX limit-count plugin via the a6 CLI. - Covers fixed-window rate limiting, count/time_window configuration, key types, - Redis and Redis-cluster policies for distributed limiting, group-based shared - quotas, consumer-level vs route-level limiting, response headers, and common - operational patterns. + Skill for configuring the APISIX limit-count plugin via the a6 CLI. Covers + fixed and sliding windows, Redis Sentinel, delayed sync, and shared quotas. version: "1.0.0" author: Apache APISIX Contributors license: Apache-2.0 @@ -24,11 +21,16 @@ metadata: ## Overview -The `limit-count` plugin rate-limits requests using a fixed-window counter -algorithm. Define a maximum number of requests (`count`) within a time interval -(`time_window`). Supports per-IP, per-consumer, per-header, or custom variable -keys. For distributed APISIX deployments, use Redis or Redis-cluster as the -shared counter backend. +The `limit-count` plugin rate-limits requests using a counter in a time window. +Define a maximum number of requests (`count`) within an interval (`time_window`). +The default `window_type` is `fixed`. Set `window_type: sliding` to smooth bursts +at window boundaries. Supports per-IP, per-consumer, per-header, or custom +variable keys. For distributed APISIX deployments, share counters through Redis, +Redis Cluster, or Redis Sentinel (`policy: redis-sentinel`). + +Redis Sentinel, sliding windows, and delayed Redis synchronization (`sync_interval`) +are available from APISIX 3.18.0. Field tables and examples: +https://docs.api7.ai/hub/limit-count ## When to Use @@ -50,7 +52,9 @@ shared counter backend. | `rejected_code` | integer | No | `503` | HTTP status on rejection (200–599) | | `rejected_msg` | string | No | — | Custom rejection message body | | `group` | string | No | — | Share counters across routes with same group ID | -| `policy` | string | No | `"local"` | Storage: `"local"`, `"redis"`, or `"redis-cluster"` | +| `policy` | string | No | `"local"` | Storage: `"local"`, `"redis"`, `"redis-cluster"`, or `"redis-sentinel"` | +| `window_type` | string | No | `"fixed"` | `"fixed"` or `"sliding"` (APISIX 3.18.0+) | +| `sync_interval` | number | No | `-1` | Redis sync interval in seconds. `-1` syncs every request. Min `0.1` when enabled; must be smaller than a numeric `time_window` | | `show_limit_quota_header` | boolean | No | `true` | Include X-RateLimit-* headers in responses | | `allow_degradation` | boolean | No | `false` | Allow requests when plugin fails | @@ -78,6 +82,22 @@ shared counter backend. | `redis_timeout` | integer | No | `1000` | Timeout in milliseconds | | `redis_cluster_ssl` | boolean | No | `false` | Enable TLS | +### Redis Sentinel Fields (when `policy: "redis-sentinel"`, APISIX 3.18.0+) + +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `redis_sentinels` | array[object] | **Yes** | — | Sentinel nodes: `{ "host": "...", "port": 26379 }` | +| `redis_master_name` | string | **Yes** | — | Sentinel-monitored master name | +| `redis_role` | string | No | `"master"` | `"master"` or `"slave"` | +| `redis_username` | string | No | — | Redis ACL username | +| `redis_password` | string | No | — | Redis password | +| `redis_database` | integer | No | `0` | Redis database index | +| `sentinel_username` | string | No | — | Redis Sentinel ACL username | +| `sentinel_password` | string | No | — | Redis Sentinel password | +| `redis_connect_timeout` | integer | No | `1000` | Connection timeout in milliseconds | +| `redis_read_timeout` | integer | No | `1000` | Read timeout in milliseconds | +| `redis_keepalive_timeout` | integer | No | `60000` | Keepalive timeout in milliseconds | + ## Key Types | `key_type` | `key` Format | Example | Description | @@ -234,6 +254,51 @@ All routes with `"group": "api-v1"` share the same 1000 req/hour counter. Use Redis when running multiple APISIX nodes to share counters. +### Redis Sentinel + +```json +{ + "plugins": { + "limit-count": { + "count": 1000, + "time_window": 60, + "key": "remote_addr", + "policy": "redis-sentinel", + "redis_master_name": "mymaster", + "redis_sentinels": [ + { "host": "192.168.1.10", "port": 26379 }, + { "host": "192.168.1.11", "port": 26379 } + ], + "rejected_code": 429 + } + } +} +``` + +### Sliding window and delayed Redis sync + +```json +{ + "plugins": { + "limit-count": { + "count": 1000, + "time_window": 60, + "window_type": "sliding", + "policy": "redis", + "redis_host": "redis.example.com", + "sync_interval": 1, + "rejected_code": 429 + } + } +} +``` + +`sync_interval` also works with `redis-cluster` and `redis-sentinel`. A numeric +`time_window` must be greater than `sync_interval`, or APISIX rejects the plugin +configuration. If a variable-based `time_window` resolves to a value less than +or equal to `sync_interval` at request time, APISIX falls back to per-request +synchronization. + ### Redis cluster ```json @@ -261,20 +326,20 @@ Use Redis when running multiple APISIX nodes to share counters. | Symptom | Cause | Fix | |---------|-------|-----| -| Limits not shared across APISIX nodes | Using `policy: "local"` (default) | Switch to `"redis"` or `"redis-cluster"` | +| Limits not shared across APISIX nodes | Using `policy: "local"` (default) | Switch to `"redis"`, `"redis-cluster"`, or `"redis-sentinel"` | | Group config rejected | Mismatched configs in same group | Ensure all routes in group have identical limit-count config | -| Unexpected counter reset | Fixed-window boundary | Normal behavior — counters reset at fixed intervals | +| Unexpected counter reset | Fixed-window boundary | Normal for `window_type: fixed`; use `"sliding"` to smooth bursts | | Key empty, all clients share one counter | Variable doesn't exist | Verify key variable name; falls back to `remote_addr` | | Rate limit headers missing | `show_limit_quota_header: false` | Set to `true` (default) | | 503 instead of 429 | Default `rejected_code` is 503 | Set `rejected_code: 429` explicitly | ## Fixed-Window Algorithm Note -`limit-count` uses a fixed-window algorithm. Counters reset at exact intervals. -This means a burst at the boundary of two windows can temporarily exceed the -intended rate (e.g., 100 req/min allows 200 requests if 100 come at t=59s and -100 at t=61s). For smoother rate limiting, combine with `limit-req` (leaky -bucket). +`limit-count` defaults to a fixed-window algorithm. Counters reset at exact +intervals, so a burst at the boundary of two windows can temporarily exceed the +intended rate (for example, 100 req/min allows 200 requests if 100 come at +t=59s and 100 at t=61s). Set `window_type: sliding` to weight the previous +window, or combine with `limit-req` (leaky bucket). ## Config Sync Example diff --git a/skills/a6-plugin-openid-connect/SKILL.md b/skills/a6-plugin-openid-connect/SKILL.md index 7496b12..5444ea1 100644 --- a/skills/a6-plugin-openid-connect/SKILL.md +++ b/skills/a6-plugin-openid-connect/SKILL.md @@ -1,10 +1,9 @@ --- name: a6-plugin-openid-connect description: >- - Skill for configuring the Apache APISIX openid-connect plugin via the a6 CLI. - Covers OIDC authorization code flow, bearer token validation, token - introspection vs JWKS verification, session management, provider setup for - Keycloak/Auth0/Okta, redirect URIs, and common operational patterns. + Skill for configuring the APISIX openid-connect plugin via the a6 CLI. + Covers authorization-code and bearer flows, PAR, DPoP, and session + validation. version: "1.0.0" author: Apache APISIX Contributors license: Apache-2.0 @@ -26,6 +25,18 @@ identity providers (Keycloak, Auth0, Okta, etc.). It supports the full authorization code flow for browser-based applications, bearer token validation for API clients, and token introspection or local JWKS verification. +From APISIX 3.18.0 the plugin also supports nested PAR and DPoP configuration, +forwards the raw ID token when `set_raw_id_token_header` is enabled, fails closed +when the trusted issuer cannot be determined, treats +`claim_validator.audience.match_with_client_id` as requiring an audience claim, +and enforces `required_scopes` on authorization-code sessions. For bearer JWT +validation, configure `claim_validator.issuer.valid_issuers` when discovery can +be unavailable. + +Field tables and a Keycloak PAR/DPoP walkthrough: +https://docs.api7.ai/hub/openid-connect +https://docs.api7.ai/apisix/how-to-guide/authentication/secure-oidc-with-par-and-dpop + ## When to Use - Integrate with enterprise identity providers (Keycloak, Auth0, Okta, Azure AD) @@ -40,7 +51,7 @@ for API clients, and token introspection or local JWKS verification. | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `client_id` | string | **Yes** | — | OAuth 2.0 client ID | -| `client_secret` | string | **Yes** | — | OAuth 2.0 client secret (encrypted in etcd) | +| `client_secret` | string | Conditional | — | OAuth 2.0 client secret (encrypted in etcd). Optional for local JWT verification, `private_key_jwt`, or a public-client PKCE flow | | `discovery` | string | **Yes** | — | OIDC well-known discovery URL | ### Authentication & Scopes @@ -75,7 +86,8 @@ for API clients, and token introspection or local JWKS verification. | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `session.secret` | string | Yes* | — | 16+ char key for session encryption (*required for auth code flow) | -| `session.cookie.lifetime` | integer | No | `3600` | Session cookie lifetime in seconds | +| `session.absolute_timeout` | integer | No | — | Absolute session lifetime in seconds | +| `session.cookie.lifetime` | integer | No | — | Deprecated alias for `session.absolute_timeout` | | `session.storage` | string | No | `"cookie"` | `"cookie"` or `"redis"` | ### Headers to Upstream @@ -85,14 +97,27 @@ for API clients, and token introspection or local JWKS verification. | `set_access_token_header` | boolean | No | `true` | Set `X-Access-Token` header | | `access_token_in_authorization_header` | boolean | No | `false` | Set token in `Authorization` header | | `set_id_token_header` | boolean | No | `true` | Set `X-ID-Token` header | +| `set_raw_id_token_header` | boolean | No | `false` | Set `X-Raw-ID-Token` with the unmodified ID token | | `set_userinfo_header` | boolean | No | `true` | Set `X-Userinfo` header | | `hide_credentials` | boolean | No | `false` | Remove auth headers before upstream | +### PAR and DPoP (APISIX 3.18.0+) + +Configure these as nested objects. Flat keys such as `use_par` or `use_dpop` are +rejected. + +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `par.enabled` | boolean | No | `false` | Send the authorization request through PAR | +| `dpop.enabled` | boolean | No | `false` | Bind token requests with a DPoP proof JWT | + +See the Plugin Hub page for endpoint auth, key material, and validation rules. + ### Advanced | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| -| `ssl_verify` | boolean | No | `false` | Verify IdP SSL certificates | +| `ssl_verify` | boolean | No | `true` | Verify IdP SSL certificates | | `timeout` | integer | No | `3` | Request timeout to IdP in seconds | | `use_pkce` | boolean | No | `false` | Enable PKCE (RFC 7636) | | `renew_access_token_on_expiry` | boolean | No | `true` | Auto-refresh expiring tokens | @@ -313,6 +338,8 @@ through without identity. | SSL errors to IdP | `ssl_verify: true` but certs invalid | Fix certs or set `ssl_verify: false` for testing | | Large cookie errors | Session too big for cookie | Switch to `session.storage: "redis"` | | Token not refreshing | `renew_access_token_on_expiry: false` | Set to `true` (default) | +| `403` with `required_scopes` after login | Session scopes missing or unreadable (3.18.0+) | Confirm granted scopes on the access or ID token; `required_scopes` applies to authorization-code sessions | +| Bearer JWT rejected while discovery is down | Issuer fail-closed (3.18.0+) | Set `claim_validator.issuer.valid_issuers` | | NGINX buffer errors | Session cookie too large | Increase `proxy_buffers` / `proxy_buffer_size` in NGINX config | ## Config Sync Example diff --git a/skills/a6-plugin-prometheus/SKILL.md b/skills/a6-plugin-prometheus/SKILL.md index 722eefa..c5c832e 100644 --- a/skills/a6-plugin-prometheus/SKILL.md +++ b/skills/a6-plugin-prometheus/SKILL.md @@ -1,10 +1,8 @@ --- name: a6-plugin-prometheus description: >- - Skill for configuring the Apache APISIX prometheus plugin via the a6 CLI. - Covers enabling Prometheus metrics export on routes and globally, exposed - metrics (HTTP status, latency, bandwidth, upstream health, LLM tokens), - custom labels, histogram buckets, and Grafana dashboard integration. + Skill for configuring APISIX prometheus via the a6 CLI. Covers HTTP, LLM, + and AI cache metrics, latency type labels, and Grafana dashboards. version: "1.0.0" author: Apache APISIX Contributors license: Apache-2.0 @@ -24,8 +22,9 @@ metadata: The `prometheus` plugin exposes APISIX metrics in Prometheus text format. It tracks HTTP status codes, request latency, bandwidth, upstream health, etcd -status, and (since v3.15) LLM token usage. Prometheus scrapes the metrics -endpoint; Grafana visualizes them. +status, stream sessions, LLM token usage, and AI cache hits. Prometheus scrapes +the metrics endpoint; Grafana visualizes them. Field tables: +https://docs.api7.ai/hub/prometheus ## When to Use @@ -61,15 +60,33 @@ The plugin has minimal per-route config. Most configuration is global via | `apisix_shared_dict_capacity_bytes` | gauge | Shared memory capacity | | `apisix_shared_dict_free_space_bytes` | gauge | Shared memory free space | | `apisix_stream_connection_total` | counter | TCP/UDP stream connections | +| `apisix_stream_active_connections` | gauge | Active stream connections (APISIX-Runtime, 3.18.0+) | +| `apisix_stream_status` | counter | Completed stream sessions by status (3.18.0+) | +| `apisix_stream_bandwidth` | counter | Stream bytes by direction (APISIX-Runtime, 3.18.0+) | ### LLM/AI Metrics (v3.15+) | Metric | Type | Description | |--------|------|-------------| -| `apisix_llm_latency` | histogram | LLM request latency | +| `apisix_llm_latency` | histogram | LLM request latency. From APISIX 3.18.0 the `type` label is `total` (full response) or `ttft` (time to first token on streaming). Queries that omit `type` match both; use `type="total"` for the previous total-latency meaning. Each streaming request records one `total` and one `ttft` sample | | `apisix_llm_prompt_tokens` | counter | Prompt tokens consumed | | `apisix_llm_completion_tokens` | counter | Completion tokens consumed | | `apisix_llm_active_connections` | gauge | Active LLM connections | +| `apisix_llm_prompt_tokens_dist` | histogram | Prompt-token distribution (3.18.0+) | +| `apisix_llm_completion_tokens_dist` | histogram | Completion-token distribution (3.18.0+) | + +### AI Cache Metrics (3.18.0+) + +| Metric | Type | Description | +|--------|------|-------------| +| `apisix_ai_cache_hits_total` | counter | Cache hits by exact or semantic `layer` | +| `apisix_ai_cache_misses_total` | counter | Cache misses | +| `apisix_ai_cache_bypasses_total` | counter | Lookups skipped | +| `apisix_ai_cache_embedding_latency` | histogram | Semantic-cache embedding latency | + +To drop high-cardinality labels, set `disabled_labels` in prometheus plugin +metadata. Do not disable structural labels such as `code` on HTTP status, `type` +on latency/bandwidth/LLM latency, or `layer` on cache hits. ### Latency Types @@ -132,8 +149,14 @@ scrape_configs: ### 5. Import Grafana dashboard -Import dashboard ID **11719** from grafana.com for a pre-built APISIX -monitoring dashboard. +Download the dashboard JSON that matches the APISIX version, for example: + +``` +https://raw.githubusercontent.com/apache/apisix/3.18.0/docs/assets/other/json/apisix-grafana-dashboard.json +``` + +Grafana.com dashboard 11719 targets APISIX 2.10.x and legacy panels. Do not use +it for current metrics. ## Common Patterns @@ -208,4 +231,5 @@ routes: | Metrics port unreachable | `enable_export_server: false` | Set to `true` or use `public-api` plugin | | Missing route labels | `prefer_name: false` and route has no name | Set `prefer_name: true` and name your routes | | No LLM metrics | APISIX < 3.15 or ai-proxy not configured | Upgrade APISIX; ensure ai-proxy is on the route | -| High cardinality | Too many extra labels | Reduce `extra_labels`; avoid high-cardinality variables | +| High cardinality | Too many extra labels | Reduce `extra_labels`; use `disabled_labels` in plugin metadata; avoid high-cardinality variables | +| LLM latency looks doubled / wrong p99 | Selector omits `type` after 3.18.0 | Filter `apisix_llm_latency{type="total"}` |