Skip to content

[AISAGE-735] feat(ai-gateway): send explicit session ID and affinity to Vercel - #8016

Merged
chrarnoldus merged 2 commits into
mainfrom
ai-gateway/vercel-session-affinity
Oct 19, 2026
Merged

chrarnoldus merged 2 commits into
mainfrom
ai-gateway/vercel-session-affinity

Conversation

@chrarnoldus

@chrarnoldus chrarnoldus commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Every request routed to Vercel AI Gateway now sets providerOptions.gateway.sessionId to the conversation's existing ID and sets sessionAffinity: true. This happens in both branches of applyVercelSettings: user BYOK and convertProviderOptions.

Until now we sent no session ID. Vercel then fell back to prompt_cache_key, which pins only the provider and not the credential, or, on Anthropic Messages, to a fingerprint of the conversation. With an explicit ID, Vercel pins the exact credential that served the session's last successful request and puts it ahead of our order. That keeps the prompt cache warm after a fallback, for example from Vertex to Bedrock. See the session affinity docs.

Where the session ID comes from (no new ID is created):

  • chat_completions / responses: body.prompt_cache_key. applyTrackingIds already sets it to the user- and task-scoped hash generateProviderSpecificSessionHash(userId, taskId, vercel). If there is no task ID, it falls back to the client's own key, and applyTrackingIds hashes keys longer than 64 characters.
  • messages: body.session_id. Until now applyTrackingIds filled this field, and the hashed user, for OpenRouter only. It now does the same for Vercel, so Messages requests carry the same session hash and user hash as the other request kinds. chat_completions and responses already sent user to every provider.
  • If no ID exists, sessionId is left unset. sessionAffinity: true is still sent.

VercelProviderConfig['gateway'] now declares sessionId?: string and sessionAffinity?: boolean. The GatewayProviderOptions type in @ai-sdk/gateway@4.0.21 accepts them only through its [key: string]: unknown index signature. The order/only logic is unchanged.

Verification

I sent live requests to https://ai-gateway.vercel.sh/v1 with our production key. The bodies were built by hand to match what the new code produces; they did not go through the gateway route itself.

  • Vertex → Bedrock fallback, then affinity. Messages, anthropic/claude-sonnet-4.5, order/only: [vertexAnthropic, bedrock], explicit sessionId and sessionAffinity: true. To force the fallback, request 1 set a 1000 ms BYOK timeout on vertexAnthropic.
    • Request 1: vertexAnthropic failed and bedrock served it. clientSessionIdSource: "explicit", affinity.outcome: "no_pin".
    • Requests 2 and 3 (no timeout, Vertex still first in order): bedrock served both. clientSessionIdSource: "explicit", affinity.outcome: "promoted", pinnedProvider: "bedrock", displacedProvider: "vertexAnthropic".
    • A session that stays on Vertex reports "confirmed" with pinnedProvider: "vertexAnthropic".
  • Top-level user on Messages does not break requests. All returned 200:
    • claude-haiku-4.5 pinned to each of anthropic (BYOK), bedrock (BYOK), vertexAnthropic (BYOK) and claudeaws (system).
    • Messages requests to openai/gpt-5-mini, moonshotai/kimi-k2 (served by novita) and google/gemini-2.5-flash-lite (served by vertex).
    • A streamed Messages request.
  • chat_completions and responses with prompt_cache_key plus providerOptions.gateway.sessionId return 200 with clientSessionIdSource: "explicit".

Automated: all packages/web-shared/src/lib/ai-gateway Jest suites pass (104 suites, 1977 tests), and so do pnpm --filter @kilocode/web-shared typecheck and oxlint.

Visual Changes

N/A

Reviewer Notes

  • Failure UX: a new pin moves the pinned provider ahead of order, but only among providers our only/ZDR filters already allow, and never across the BYOK/system-credential boundary. If the pinned provider fails, Vercel removes the pin and falls back as before, so genuine failures take no longer to surface.
  • Without an x-kilocode-taskid, a client-supplied prompt_cache_key (64 characters or fewer) now becomes an explicit session ID, so Vercel pins the credential and not just the provider. Messages requests already forwarded a client-supplied session_id to Vercel as the top-level field.

@chrarnoldus chrarnoldus self-assigned this Oct 19, 2026
@kilo-code-bot

kilo-code-bot Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

The follow-up commit only adds the Vercel provider to the messages-request user hash branch; behavior is type-safe, matches the Vercel safety-identifier helper, and is covered by the updated unit test.

Files Reviewed (2 files)

  • packages/web-shared/src/lib/ai-gateway/providerHash.test.ts
  • packages/web-shared/src/lib/ai-gateway/providerHash.ts
Previous Review Summary (commit 0061e96)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 0061e96)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (5 files)
  • packages/web-shared/src/lib/ai-gateway/providerHash.test.ts
  • packages/web-shared/src/lib/ai-gateway/providerHash.ts
  • packages/web-shared/src/lib/ai-gateway/providers/openrouter/types.ts
  • packages/web-shared/src/lib/ai-gateway/providers/vercel/index.test.ts
  • packages/web-shared/src/lib/ai-gateway/providers/vercel/index.ts

Reviewed by deepseek-v4.1-flash-1017 · Input: 39.1K · Output: 3.2K · Cached: 323.3K

Review guidance: REVIEW.md from base branch main

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