Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ jobs:
for path in index.html zh/index.html _astro \
blog zh/blog learning-center zh/learning-center \
articles zh/articles events zh/events \
integrations zh/integrations cookbooks zh/cookbooks \
comparisons zh/comparisons \
ai-gateway zh/ai-gateway \
plugins zh/plugins downloads zh/downloads; do
Expand Down Expand Up @@ -469,6 +470,10 @@ jobs:
test -f website/build/learning-center/atom.xml
test -f website/build/articles/index.html
test -f website/build/zh/articles/rss.xml
test -f website/build/integrations/redis/index.html
test -f website/build/zh/integrations/redis/index.html
test -f website/build/cookbooks/redis-ai-cache/index.html
test -f website/build/zh/cookbooks/redis-ai-cache/index.html
test -f website/build/events/archive/index.html
test -f website/build/docs/general/events/index.html
# The comparisons hub (a single index.html in both locales) is Astro's.
Expand Down Expand Up @@ -507,6 +512,8 @@ jobs:
blog_post_twin=$(find website/build/blog -mindepth 2 -type f -name index.md -print -quit)
test -n "$blog_post_twin"
test -f website/build/docs/apisix/plugins/cors/index.md
test -f website/build/integrations/redis/index.md
test -f website/build/cookbooks/redis-ai-cache/index.md
grep -q 'index.md' website/build/llms.txt
test -f website/build/img/integrations/icon-prometheus.svg
test -f website/build/img/architecture.svg
Expand Down Expand Up @@ -580,6 +587,8 @@ jobs:
run: |
node next/scripts/generate-sitemaps.mjs --dist website/build
grep -q '<loc>https://apisix.apache.org/learning-center/mcp-protocol-ai-gateway/</loc>' website/build/sitemap.xml
grep -q '<loc>https://apisix.apache.org/integrations/redis/</loc>' website/build/sitemap.xml
grep -q '<loc>https://apisix.apache.org/cookbooks/redis-ai-cache/</loc>' website/build/sitemap.xml
grep -q '<loc>https://apisix.apache.org/zh/learning-center/</loc>' website/build/zh/sitemap.xml
if grep -q '<loc>https://apisix.apache.org/zh/learning-center/what-is-an-api-gateway/</loc>' website/build/zh/sitemap.xml; then
echo 'Retired English-only Chinese learning-center URL remains in the sitemap.'
Expand Down
8 changes: 8 additions & 0 deletions blog/i18n/zh/docusaurus-theme-classic/navbar.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@
"message": "相关资源",
"description": "Navbar item with label Resources"
},
"item.label.Integrations": {
"message": "集成",
"description": "Navbar item with label Integrations"
},
"item.label.Cookbooks": {
"message": "Cookbook",
"description": "Navbar item with label Cookbooks"
},
"item.label.PluginHub": {
"message": "插件市场",
"description": "Navbar item with label Plugin Hub"
Expand Down
10 changes: 10 additions & 0 deletions config/navbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,16 @@ module.exports = [
label: 'Resources',
position: 'right',
items: [
{
to: '/integrations',
label: 'Integrations',
target: '_parent',
},
{
to: '/cookbooks',
label: 'Cookbooks',
target: '_parent',
},
{
to: '/plugins',
label: 'Plugin Hub',
Expand Down
8 changes: 8 additions & 0 deletions doc/i18n/zh/docusaurus-theme-classic/navbar.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@
"message": "相关资源",
"description": "Navbar item with label Resources"
},
"item.label.Integrations": {
"message": "集成",
"description": "Navbar item with label Integrations"
},
"item.label.Cookbooks": {
"message": "Cookbook",
"description": "Navbar item with label Cookbooks"
},
"item.label.PluginHub": {
"message": "插件市场",
"description": "Navbar item with label Plugin Hub"
Expand Down
10 changes: 10 additions & 0 deletions examples/redis-ai-gateway/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# scripts/setup.sh generates an isolated Compose instance ID and the three local secrets below.
LAB_INSTANCE_ID=
REDIS_PASSWORD=
CONSUMER_A_KEY=
CONSUMER_B_KEY=

# Required only for functional cache and quota tests. The scripts never print it.
OPENAI_API_KEY=
OPENAI_CHAT_MODEL=gpt-4o-mini
OPENAI_EMBEDDING_MODEL=text-embedding-3-small
2 changes: 2 additions & 0 deletions examples/redis-ai-gateway/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.env
results/
78 changes: 78 additions & 0 deletions examples/redis-ai-gateway/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Apache APISIX 3.18 and Redis AI Gateway lab

This lab accompanies the Redis integration page and its two cookbooks. It pins the APISIX and Redis images, keeps Redis and the APISIX management interfaces off the host, and separates infrastructure checks from tests that call a live provider.

## Pinned runtime

- Apache APISIX 3.18.0, tag commit `0796d9c2cbedb1f8bf8194292ff526599f4fde20`
- `apache/apisix:3.18.0-debian@sha256:84e6b5e787e9f889ebff88161cb9a16599bafcffa236c6b54c7f779a0655940d`
- Redis Open Source 8.10.1 with its bundled Search module explicitly loaded
- `redis:8-alpine@sha256:becdda6c7f4b3fb42e42fd7f120bbf5c54c4caaaf16f26da24e4563d2c1f0576`

The images are pinned with multi-architecture registry digests. This lab runs only on `linux/amd64` and `linux/arm64` because the bundled modules in the official Redis Open Source 8.10.1 image are available only for those architectures. When sharing results, include the platform-specific image ID.

## Run the infrastructure preflight

Requirements: Docker Compose, Bash, `awk`, `cmp`, `curl`, `jq`, and OpenSSL.

```bash
./scripts/setup.sh
```

The setup script creates a mode-`0600` `.env` with a unique Compose project ID and generated Redis and Consumer secrets. It starts two APISIX nodes and a private, ephemeral Redis instance from a clean state, then checks:

- both APISIX Status APIs report ready;
- only gateway ports `127.0.0.1:9080` and `127.0.0.1:9081` are published;
- the missing-key and valid-key authentication paths behave as expected without calling a provider;
- the Redis service is not published to the host;
- the Redis service reports version 8.10.1 and accepts `FT._LIST`;
- Compose resolved the expected immutable image digests.

This preflight does **not** call an LLM, so it does not verify caching or quota behavior end to end.

## Run the OpenAI tests

Add a dedicated, least-privilege OpenAI API key to `.env`. The tests use `gpt-4o-mini` and `text-embedding-3-small` by default. They never print the key or prompt/response bodies.

Before proxying, the Routes remove the Consumer credential and any client-supplied OpenAI organization, project, or beta header. These headers can affect the provider response but are not part of the cache key. Access logs also omit raw query strings. Send the Consumer key in the `apikey` header; never put credentials in the URL.

```dotenv
OPENAI_API_KEY=replace-me
```

Then run:

```bash
./scripts/test-shared-quota.sh
./scripts/test-cache.sh
./scripts/test-failure-modes.sh
```

The quota test sends a live request through node A and waits for APISIX to write the provider's `usage.total_tokens` value to Redis. It then checks that node B applies the same counter and returns the configured `429`.

The cache test verifies an exact cross-node hit, byte-identical response-body replay, Consumer isolation, a semantic hit for a paraphrased prompt, semantic-to-exact backfill, and an unrelated miss. It never lowers the similarity threshold automatically.

The failure test compares cache fail-open behavior with rate limiting when `allow_degradation` is `false` and when it is `true`.

## Remaining validation

The public pages will remain **Validation in progress** until the following checks are also complete:

- provider-side chat and embedding call counters, correlated to each request;
- complete and interrupted SSE cases;
- two clean runs and a rerun by another operator;
- sanitized APISIX logs proving no credential or body leakage;
- live Redis Cluster and Sentinel failover tests before either mode is marked verified;
- a documented test date, machine architecture, provider region, and model identifiers.

APISIX records token usage after the provider responds, so a large response or concurrent requests can exceed the limit. This is not a prepaid budget. In APISIX 3.18.0, `ai-cache` uses one Redis endpoint; this lab does not test cache HA.

## Cleanup

```bash
./scripts/cleanup.sh
```

Cleanup removes only the containers and network for the lab's unique Compose project. Redis has no host port and its data is not persisted. The scripts never run `FLUSHALL` against an external service. Cleanup leaves the mode-`0600`, Git-ignored `.env` in place so you can run the lab again. Delete that file when you are finished, especially if it contains a provider key.

Redis is a registered trademark of Redis Ltd. This community lab is not endorsed, supported, or certified by Redis Ltd.
69 changes: 69 additions & 0 deletions examples/redis-ai-gateway/compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: apisix-redis-ai-gateway

x-apisix-common: &apisix-common
image: "apache/apisix:3.18.0-debian@\
sha256:84e6b5e787e9f889ebff88161cb9a16599bafcffa236c6b54c7f779a0655940d"
environment:
REDIS_PASSWORD: "${REDIS_PASSWORD:?run scripts/setup.sh first}"
CONSUMER_A_KEY: "${CONSUMER_A_KEY:?run scripts/setup.sh first}"
CONSUMER_B_KEY: "${CONSUMER_B_KEY:?run scripts/setup.sh first}"
OPENAI_API_KEY: "${OPENAI_API_KEY:-not-configured}"
OPENAI_AUTHORIZATION: "Bearer ${OPENAI_API_KEY:-not-configured}"
OPENAI_CHAT_MODEL: "${OPENAI_CHAT_MODEL:-gpt-4o-mini}"
OPENAI_EMBEDDING_MODEL: "${OPENAI_EMBEDDING_MODEL:-text-embedding-3-small}"
volumes:
- ./conf/config.yaml:/usr/local/apisix/conf/config.yaml:ro
- ./conf/apisix.yaml:/usr/local/apisix/conf/apisix.yaml:ro
depends_on:
redis:
condition: service_healthy
networks:
- lab
healthcheck:
test:
- CMD-SHELL
- >-
bash -ec 'exec 3<>/dev/tcp/127.0.0.1/7085;
printf "GET /status/ready HTTP/1.0\r\nHost: localhost\r\n\r\n" >&3;
IFS= read -r line <&3;
[[ "$$line" == *" 200 "* ]]'
interval: 2s
timeout: 2s
retries: 30
start_period: 10s

services:
redis:
image: redis:8-alpine@sha256:becdda6c7f4b3fb42e42fd7f120bbf5c54c4caaaf16f26da24e4563d2c1f0576
environment:
REDIS_PASSWORD: "${REDIS_PASSWORD:?run scripts/setup.sh first}"
command:
- sh
- -ec
- >-
exec redis-server --requirepass "$${REDIS_PASSWORD}"
--appendonly no --save ''
--loadmodule /usr/local/lib/redis/modules/redisearch.so
networks:
- lab
healthcheck:
test:
- CMD-SHELL
- REDISCLI_AUTH="$${REDIS_PASSWORD}" redis-cli ping | grep -q PONG
interval: 2s
timeout: 2s
retries: 30
# Redis is private and ephemeral: no host port and no persistent volume.

apisix-a:
<<: *apisix-common
ports:
- "127.0.0.1:9080:9080"

apisix-b:
<<: *apisix-common
ports:
- "127.0.0.1:9081:9080"

networks:
lab: {}
Loading
Loading