Skip to content

U8: k6 load tests update#12

Open
jaturapornchai wants to merge 1 commit into
mainfrom
worktree-agent-a61ee34f
Open

U8: k6 load tests update#12
jaturapornchai wants to merge 1 commit into
mainfrom
worktree-agent-a61ee34f

Conversation

@jaturapornchai

Copy link
Copy Markdown
Owner

Summary

  • Add aggressive thresholds to smoke/chat/stress: p(95)<5s, p(99)<15s (<10s for stress), http_req_failed rate<0.02.
  • Rotate 4 diverse prompt categories per VU iteration (thai/code/general, small/medium) via shared loadtest/_categories.js helper; each request tagged with category for per-category metric breakdown.
  • Smoke now also hits /v1/chat/completions (5 VUs / 30s). Chat ramps 0->20->0 VUs. Stress ramps 0->50->100->0 VUs with a per-request chat_req_duration Trend retained.

Test plan

  • rtk npx next build -> Errors: 0 | Warnings: 0
  • k6 run loadtest/smoke.js against a running bcproxyai (BASE_URL=http://localhost:3334)
  • k6 run loadtest/chat.js -> thresholds pass under normal load
  • k6 run loadtest/stress.js -> observe aggressive-mode latency under 100 VUs

Note: pre-existing vitest failures in scanner.test.ts (mock config) are unrelated to this JS-only loadtest change.

Generated with Claude Code

Add p95<5s / p99<15s (10s for stress) thresholds and <2% error rate
across smoke/chat/stress. Each script now rotates through 4 diverse
prompt categories (thai/code/general, small/medium) via a shared
_categories.js helper, tagged per request for per-category metrics.
Smoke exercises the chat endpoint in addition to homepage/status;
chat ramps to 20 VUs; stress caps at 100 VUs (previously 500) to
surface aggressive-mode latency without swamping the host.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 9, 2026 21:44

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the k6 load-testing suite for BCProxyAI to add stricter performance/error thresholds, broaden coverage (smoke now exercises chat completions), and introduce per-request prompt “category” tagging to segment metrics by prompt type/size.

Changes:

  • Tightened k6 thresholds across smoke/chat/stress (p95/p99 latency + http_req_failed rate) and adjusted VU ramp profiles.
  • Added shared pickCategory() helper and tagged each chat request with a category for per-category metric breakdown.
  • Smoke test now includes a /v1/chat/completions request in addition to basic health endpoints.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
loadtest/_categories.js Adds shared prompt categories and a helper to pick one per request.
loadtest/smoke.js Increases load, adds thresholds, and adds a chat completion request with category tagging.
loadtest/chat.js Adjusts ramp profile and thresholds; uses shared categories and tags requests.
loadtest/stress.js Changes ramp profile, adds strict thresholds, and switches to shared categories + request tags while retaining Trend.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread loadtest/smoke.js
Comment on lines +7 to +11
vus: 5,
duration: '30s',
thresholds: {
http_req_failed: ['rate<0.01'],
'http_req_duration{expected_response:true}': ['p(95)<5000', 'p(99)<15000'],
'http_req_failed': ['rate<0.02'],

Copilot AI Apr 9, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the server-side rate limit (100 req/60s per IP on /v1/chat/completions), running 5 VUs for 30s will very likely produce a large number of 429s, causing the new http_req_failed rate<0.02 threshold to fail consistently. Consider lowering VUs/iteration rate to stay under the limiter, or explicitly treating 429 as an expected status in k6 (e.g., via a responseCallback/expectedStatuses) or by varying X-Real-IP/X-Forwarded-For per VU if the goal is to measure backend latency rather than the limiter.

Copilot uses AI. Check for mistakes.
Comment thread loadtest/smoke.js
);
check(res, {
'chat status 200': (r) => r.status === 200,
'chat body has content': (r) => r.body && r.body.includes('content'),

Copilot AI Apr 9, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chat body has content uses a string substring check on the raw response body, which can produce false positives (e.g., error payloads that include the word "content") and false negatives (format changes). It’s more reliable to parse JSON (e.g., res.json() / selector) and assert the expected shape like choices[0].message.content (with a guard for JSON parse errors).

Suggested change
'chat body has content': (r) => r.body && r.body.includes('content'),
'chat body has content': (r) => {
if (!r.body) {
return false;
}
try {
const body = JSON.parse(r.body);
return Array.isArray(body.choices)
&& body.choices.length > 0
&& body.choices[0]
&& body.choices[0].message
&& typeof body.choices[0].message.content === 'string'
&& body.choices[0].message.content.length > 0;
} catch (_) {
return false;
}
},

Copilot uses AI. Check for mistakes.
Comment thread loadtest/chat.js
Comment on lines 6 to 14
stages: [
{ duration: '30s', target: 10 },
{ duration: '1m', target: 10 },
{ duration: '30s', target: 30 },
{ duration: '1m', target: 30 },
{ duration: '30s', target: 20 },
{ duration: '1m', target: 20 },
{ duration: '30s', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<10000'],
http_req_failed: ['rate<0.15'],
'http_req_duration{expected_response:true}': ['p(95)<5000', 'p(99)<15000'],
'http_req_failed': ['rate<0.02'],
},

Copilot AI Apr 9, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given /v1/chat/completions is rate-limited to 100 req/60s per IP, the stage (20 VUs with ~1–3s think time) will exceed the limiter and generate many 429s. That will violate http_req_failed rate<0.02 (since 429 counts as failed by default), so the test is likely to fail in normal local/staging runs even though the script backs off on 429. Consider either (a) lowering the request rate below the limiter, or (b) marking 429 as an expected status in k6 so it doesn’t count toward http_req_failed, depending on what this test is intended to validate.

Copilot uses AI. Check for mistakes.
Comment thread loadtest/stress.js
Comment on lines +13 to +16
thresholds: {
'http_req_duration{expected_response:true}': ['p(95)<5000', 'p(99)<10000'],
'http_req_failed': ['rate<0.02'],
},

Copilot AI Apr 9, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new http_req_failed rate<0.02 threshold will likely fail quickly under stress conditions because /v1/chat/completions is rate-limited (429) and 429s are counted as failures by default in k6. If the goal is to stress the backend (not the limiter), consider varying X-Real-IP/X-Forwarded-For per VU or adjusting expected statuses/thresholds so rate limiting doesn’t dominate the failure signal.

Copilot uses AI. Check for mistakes.
Comment thread loadtest/_categories.js
Comment on lines +10 to +11
export function pickCategory() {
return categories[Math.floor(Math.random() * categories.length)];

Copilot AI Apr 9, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description says categories are “rotated” per VU iteration, but pickCategory() currently selects randomly, which can skew per-category breakdowns and make runs less reproducible. If rotation is required, consider a deterministic selection (e.g., based on __ITER/__VU) to cycle through categories evenly.

Copilot uses AI. Check for mistakes.
jaturapornchai added a commit that referenced this pull request Apr 21, 2026
/guide Hermes snippet was based on old CLI (hermes model add …, config.toml)
that doesn't match today's Hermes v0.10.0. Replaced with the exact commands
verified on a fresh Ubuntu 24.04 (WSL2) install — including the
provider=custom trick that redirects Hermes away from openrouter.ai.

Also bumps README:
- Smart Routing: 13 → 15 steps. Added #12 Mistral message-order auto-patch
  + #13 Client-400 short-circuit (both shipped today as fixes for
  Hermes/OpenClaw compatibility).
- Feature table: new 🤖 Agent-ready row.
- Integration: new "Hermes Agent (Nous Research)" subsection with
  install + config + one-liner demo.

Verified in production: 13/16 built-in Hermes tools pass cleanly through
SMLGateway (terminal, file RW, code_execution, todo, vision, tts, clarify,
session_search, skills, delegation, moa). 3 require extra config
(memory-persist must be explicit, cronjob needs gateway service, delegation
subagent self-corrects unknown tool names — all non-gateway issues).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants