-
Notifications
You must be signed in to change notification settings - Fork 108
Sample/ai gateway observability #3334
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Dimagidhp
wants to merge
8
commits into
wso2:main
Choose a base branch
from
Dimagidhp:sample/ai-gateway-observability
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 7 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
c36f253
Add sample for AI gateway metrics and tracing
Dimagidhp 7f377e3
refactor: streamline comments and improve clarity in configuration sc…
Dimagidhp 0bc3bbc
Resolved coderabbit comments
Dimagidhp 019e7fe
Updated README for clarity on setup and usage
Dimagidhp 334a2bb
fix: clarify API key references in README for better understanding
Dimagidhp ea0122d
Use the gateway's Prometheus scrape targets in the observability sample
Dimagidhp 1b15a05
Move the observability sample's CI workflow into the sample folder
Dimagidhp e675bfa
Merge branch 'main' into sample/ai-gateway-observability
Dimagidhp File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| # Keep the shell scripts LF-only in the working tree on every platform. | ||
| # Git on Windows often converts checked-out files to CRLF, and bash refuses a script | ||
| # whose shebang line ends in CR: "/usr/bin/env: 'bash\r': No such file or directory". | ||
| # Windows users run this sample inside WSL2, where that conversion would break it. | ||
| *.sh text eol=lf | ||
| *.yaml text eol=lf | ||
| *.yml text eol=lf | ||
| *.toml text eol=lf |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| name: AI Gateway Observability Sample PR Check | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| pull_request: | ||
| branches: | ||
| - main | ||
| paths: | ||
| - 'samples/ai-gateway-observability/**' | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| pr-check: | ||
| runs-on: ubuntu-24.04 | ||
| defaults: | ||
| run: | ||
| working-directory: samples/ai-gateway-observability | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - name: Install dependencies | ||
| run: | | ||
| sudo apt-get update | ||
| sudo apt-get install -y jq | ||
|
|
||
| - name: Shell syntax check | ||
| run: bash -n setup.sh load.sh test.sh teardown.sh | ||
|
|
||
| - name: Validate dashboard JSON and WireMock mappings | ||
| run: | | ||
| python3 -c "import json,glob; [json.load(open(f)) for f in glob.glob('observability/*.json') + glob.glob('wiremock/mappings/*.json')]" | ||
|
|
||
| - name: Start the gateway and observability stack | ||
| run: ./setup.sh | ||
|
|
||
| - name: Generate traffic | ||
| run: ./load.sh 45 | ||
|
|
||
| - name: Assert metrics and traces are flowing | ||
| run: ./test.sh | ||
|
|
||
| - name: Dump container logs on failure | ||
| if: failure() | ||
| run: | | ||
| docker ps -a | ||
| (cd wso2apip-ai-gateway-1.2.0 && docker compose --profile metrics --profile tracing logs --tail 200) || true | ||
|
|
||
| - name: Teardown | ||
| if: always() | ||
| run: ./teardown.sh --clean | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| # The gateway distribution downloaded and extracted by setup.sh. The zip is already | ||
| # covered by the repository's root .gitignore; the extracted directory is not, and it | ||
| # holds generated secrets (listener key, AES key, api-platform.env) that must never be | ||
| # committed. ./teardown.sh --clean removes both. | ||
| wso2apip-ai-gateway-*/ | ||
| wso2apip-ai-gateway-*.zip |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,154 @@ | ||
| # AI Gateway: Metrics and Tracing with a Ready-Made Dashboard | ||
|
|
||
| This sample runs the WSO2 AI Gateway with its full observability stack switched on | ||
| (Prometheus, Grafana, an OpenTelemetry collector and Jaeger), pointed at two LLM | ||
| proxies backed by a mock model. Generate a minute of traffic, and you get a live | ||
| dashboard showing request rate, latency and errors per proxy, plus a complete trace of | ||
| any single request through the gateway. No OpenAI API key, no cloud account, nothing | ||
| to configure by hand. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - Docker with the Compose plugin | ||
| - `curl` (or `wget`), `unzip`, `jq` and `openssl` | ||
|
|
||
| On Windows, run these from a WSL2 shell with Docker Desktop's WSL integration enabled. | ||
|
|
||
| ## Getting started | ||
|
|
||
| ```bash | ||
| ./setup.sh | ||
| ``` | ||
|
|
||
| 1. Downloads and extracts the AI Gateway distribution. | ||
| 2. Enables the Prometheus endpoints and tracing, and provisions the Grafana dashboard. | ||
| 3. Starts a WireMock container standing in for the OpenAI API. | ||
| 4. Starts the gateway together with Prometheus, Grafana, Jaeger and the OTel collector. | ||
| 5. Waits for the gateway to report healthy, then puts the mock on its network. | ||
| 6. Registers two proxies, `assistant-proxy` and `support-proxy`, each with an inbound | ||
| API key. Only the provider behind `support-proxy` has a token budget. | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| Credentials, certificates and the environment file the stack needs are generated in | ||
| step 4, so there is nothing to configure beforehand. | ||
|
|
||
| ```bash | ||
| ./load.sh | ||
| ``` | ||
|
|
||
| 1. Checks the gateway is running, and stops if it is not. | ||
| 2. Sends a request every 0.25 seconds for 60 seconds, alternating between the two | ||
| proxies. Pass a different duration as `./load.sh 120`. | ||
| 3. Cycles through a fixed pattern every ten requests: one answered slowly, one failing | ||
| upstream, one with an invalid key, and seven ordinary ones. The same every run. | ||
| 4. Counts every response by status code and prints the totals. | ||
|
|
||
| Then open the two URLs the scripts print: | ||
|
|
||
| | URL | What you see | | ||
| |-----|--------------| | ||
| | <http://localhost:3000> | **Grafana**: the AI Gateway Overview dashboard, live (admin / admin) | | ||
| | <http://localhost:16686> | **Jaeger**: pick the `router` service, open any trace | | ||
|
|
||
| ## What to look for | ||
|
|
||
| **In Grafana**, the dashboard opens on the AI Gateway Overview: four tiles showing peak | ||
| values, and six charts. | ||
|
|
||
| - *Request rate per proxy*: how much traffic each proxy is handling. | ||
| - *Gateway processing time per route*: how long the gateway itself takes, as a typical | ||
| time (p50) and a slow-tail time (p95). | ||
| - *End-to-end latency*: how long the whole call takes, including the model. | ||
| - *Responses by status class*: how many requests succeeded (2xx) against how many were | ||
| rejected (4xx) or failed (5xx). | ||
| - *Policy rejections by policy*: requests the gateway blocked, and which rule blocked | ||
| them. Bad keys throughout, the token budget from part-way through the run. | ||
| - *Upstream failures*: requests the model backend itself failed. | ||
|
|
||
| **In Jaeger**, open a trace to see one request broken into its steps. The charts show | ||
| totals across all traffic; a trace shows where a single slow request lost its time. | ||
|
|
||
| ## Verify from the terminal | ||
|
|
||
| `test.sh` checks the pipeline end to end: the metrics endpoints respond, Prometheus is | ||
| scraping all three of them, both proxies report per-proxy metrics, Grafana loaded the | ||
| dashboard, and Jaeger stored traces. | ||
|
|
||
| ```bash | ||
| ./test.sh | ||
| ``` | ||
|
|
||
| Expected output: | ||
|
|
||
| ``` | ||
| ══════════════════════════════════════════════════ | ||
| Pre-flight checks | ||
| ══════════════════════════════════════════════════ | ||
| [INFO] Checking gateway health at http://localhost:9094/health ... | ||
| [PASS] Gateway is healthy. | ||
|
|
||
| ══════════════════════════════════════════════════ | ||
| Test 1: Metrics endpoints respond | ||
| ══════════════════════════════════════════════════ | ||
| [PASS] Gateway controller: HTTP 200 (http://localhost:9011/metrics) | ||
| [PASS] Policy engine: HTTP 200 (http://localhost:9003/metrics) | ||
| [PASS] Envoy router: HTTP 200 (http://localhost:9901/stats/prometheus) | ||
| ... | ||
| [PASS] Observability pipeline is working end to end. | ||
| ``` | ||
|
|
||
| ## How it works | ||
|
|
||
| ``` | ||
| ./load.sh ──► Gateway :8080 ──► mock LLM (WireMock) | ||
| │ | ||
| ┌────────────┴────────────┐ | ||
| │ │ | ||
| Prometheus scrapes gateway pushes | ||
| metrics every 15s traces (OpenTelemetry) | ||
| │ │ | ||
| ▼ ▼ | ||
| Grafana :3000 Jaeger :16686 | ||
| ``` | ||
|
|
||
| - **Metrics** answer how much traffic, how fast, and how often broken. Token usage is | ||
| not among them; that goes to analytics (Moesif), not metrics. | ||
| - **Traces** answer where a single request spent its time. | ||
|
|
||
| ## What's running | ||
|
|
||
| | Container | Role | Port | | ||
| |-----------|------|------| | ||
| | `gateway-controller` | Control plane, where proxies are registered | 9090 (API), 9011 (metrics) | | ||
| | `gateway-runtime` | Envoy router + policy engine, where traffic flows | 8080 (HTTP), 9901 (Envoy admin), 9003 (metrics) | | ||
| | `mock-llm-openai` | WireMock standing in for the OpenAI API | 8082 | | ||
| | `prometheus` | Scrapes and stores the metrics | 9092 | | ||
| | `grafana` | Charts them | 3000 | | ||
| | `otel-collector` | Receives spans from the gateway | 4317 / 4318 | | ||
| | `jaeger` | Stores and displays traces | 16686 | | ||
|
|
||
| ## Send your own request | ||
|
|
||
| ```bash | ||
| curl -X POST http://localhost:8080/assistant/chat/completions \ | ||
| -H "Content-Type: application/json" \ | ||
| -H "api_key: demo-assistant-key" \ | ||
| -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"hello"}]}' | ||
| ``` | ||
|
|
||
| `demo-assistant-key` is the inbound key `setup.sh` registered on the proxy; the gateway | ||
| rejects requests without it. Watch the request land on the dashboard, then find its | ||
| trace in Jaeger. The support proxy is at `/support/chat/completions` with | ||
| `demo-support-key`. | ||
|
|
||
| Ports, credentials, keys and traffic duration are all environment variables at the top | ||
| of `setup.sh` and `load.sh`. Override any of them before running. | ||
|
|
||
| ## Teardown | ||
|
|
||
| ```bash | ||
| ./teardown.sh # delete the proxies, stop the containers, drop the volumes | ||
| ./teardown.sh --clean # also remove the extracted distribution and the zip | ||
| ``` | ||
|
|
||
| `--clean` makes the next `./setup.sh` download and set up the gateway from scratch, | ||
| instead of reusing the copy already on disk. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # Merged into the distribution's configs/config.toml by setup.sh. | ||
| # | ||
| # [controller.metrics] and [policy_engine.metrics] expose the Prometheus endpoints the | ||
| # Grafana panels are built from. [tracing] exports OpenTelemetry spans to the | ||
| # otel-collector container, which forwards them to Jaeger. | ||
|
|
||
| [controller.metrics] | ||
| enabled = true | ||
| port = 9091 | ||
|
|
||
| [policy_engine.metrics] | ||
| enabled = true | ||
| port = 9003 | ||
|
|
||
| [tracing] | ||
| enabled = true | ||
| endpoint = "otel-collector:4317" | ||
| insecure = true | ||
| service_version = "1.0.0" | ||
| batch_timeout = "1s" | ||
| max_export_batch_size = 512 | ||
| # Sample every request. Fine for a demo; production deployments normally sample | ||
| # a small fraction of traffic instead. | ||
| sampling_rate = 1.0 |
34 changes: 34 additions & 0 deletions
34
samples/ai-gateway-observability/llm-provider-budgeted.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| apiVersion: gateway.api-platform.wso2.com/v1 | ||
| kind: LlmProvider | ||
| metadata: | ||
| name: mock-openai-provider-budgeted | ||
| spec: | ||
| displayName: Mock OpenAI Provider (token budget) | ||
| version: v1.0 | ||
| template: openai | ||
| context: /openai/budgeted | ||
| upstream: | ||
| # Same mock backend as llm-provider.yaml — the only difference is the token | ||
| # budget below. The support proxy points here so that, part-way through | ||
| # load.sh, it starts returning 429 while the assistant proxy keeps serving. | ||
| # That contrast is what makes the per-proxy panels worth looking at. | ||
| url: http://mock-llm-openai:8080/v1 | ||
| auth: | ||
| type: api-key | ||
| header: Authorization | ||
| value: Bearer local-mock-key | ||
| accessControl: | ||
| mode: deny_all | ||
| exceptions: | ||
| - path: /chat/completions | ||
| methods: [POST] | ||
| policies: | ||
| - name: token-based-ratelimit | ||
| version: v1 | ||
| paths: | ||
| - path: /chat/completions | ||
| methods: [POST] | ||
| params: | ||
| totalTokenLimits: | ||
| - count: 300 | ||
| duration: "1m" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| apiVersion: gateway.api-platform.wso2.com/v1 | ||
| kind: LlmProvider | ||
| metadata: | ||
| name: mock-openai-provider | ||
| spec: | ||
| displayName: Mock OpenAI Provider | ||
| version: v1.0 | ||
| template: openai | ||
| context: /openai/latest | ||
| upstream: | ||
| # WireMock stands in for api.openai.com — see wiremock/mappings/. | ||
| url: http://mock-llm-openai:8080/v1 | ||
| auth: | ||
| type: api-key | ||
| header: Authorization | ||
| value: Bearer local-mock-key | ||
| accessControl: | ||
| mode: deny_all | ||
| exceptions: | ||
| - path: /chat/completions | ||
| methods: [POST] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| apiVersion: gateway.api-platform.wso2.com/v1 | ||
| kind: LlmProxy | ||
| metadata: | ||
| name: assistant-proxy | ||
| spec: | ||
| displayName: Assistant Proxy | ||
| version: v1.0 | ||
| context: /assistant | ||
| provider: | ||
| id: mock-openai-provider | ||
| policies: | ||
| - name: api-key-auth | ||
| version: v1 | ||
| paths: | ||
| - path: /chat/completions | ||
| methods: [POST] | ||
| params: | ||
| key: api_key | ||
| in: header |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| apiVersion: gateway.api-platform.wso2.com/v1 | ||
| kind: LlmProxy | ||
| metadata: | ||
| name: support-proxy | ||
| spec: | ||
| displayName: Support Proxy | ||
| version: v1.0 | ||
| context: /support | ||
| provider: | ||
| # The budgeted provider — this proxy runs out of tokens under load. | ||
| id: mock-openai-provider-budgeted | ||
| policies: | ||
| - name: api-key-auth | ||
| version: v1 | ||
| paths: | ||
| - path: /chat/completions | ||
| methods: [POST] | ||
| params: | ||
| key: api_key | ||
| in: header |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.