Distributes bedrock requests evenly across regions (#36837)

* wip

* feat: distribute bedrock requests across regions

* fix: tests and type errors

* chore: remove AWS_BEDROCK_REGION from turbo.json

* fix: vercel oidc provider to fallback in credential chain

---------

Co-authored-by: delgado3d <27228526+delgado3d@users.noreply.github.com>
This commit is contained in:
Greg Richardson
2025-07-02 11:35:37 -06:00
committed by GitHub
parent feb5a57b32
commit ea91bf4739
10 changed files with 204 additions and 67 deletions

View File

@@ -0,0 +1,73 @@
import { describe, it, expect } from 'vitest'
import { selectBedrockRegion, bedrockRegionMap } from './bedrock'
describe('selectBedrockRegion', () => {
it('should return a valid region for a given routing key', async () => {
const region = await selectBedrockRegion('test-key')
const validRegions = Object.keys(bedrockRegionMap)
expect(validRegions).toContain(region)
})
it('should return the same region for the same routing key', async () => {
const routingKey = 'consistent-key'
const region1 = await selectBedrockRegion(routingKey)
const region2 = await selectBedrockRegion(routingKey)
expect(region1).toBe(region2)
})
it('should distribute different keys across regions', async () => {
const keys = Array.from({ length: 100 }, (_, i) => `key-${i}`)
const regions = await Promise.all(keys.map((key) => selectBedrockRegion(key)))
const uniqueRegions = new Set(regions)
const validRegions = Object.keys(bedrockRegionMap)
// Should use all regions for 100 different keys
expect(uniqueRegions.size).toEqual(validRegions.length)
})
it('should distribute keys evenly across regions', async () => {
const numKeys = 3000
const keys = Array.from({ length: numKeys }, (_, i) => `key-${i}`)
const regions = await Promise.all(keys.map((key) => selectBedrockRegion(key)))
const validRegions = Object.keys(bedrockRegionMap)
// Count occurrences of each region
const regionCounts = regions.reduce<Record<string, number>>((acc, region) => {
acc[region] = (acc[region] ?? 0) + 1
return acc
}, {})
const expectedCountPerRegion = numKeys / validRegions.length
const tolerance = expectedCountPerRegion * 0.2 // Allow 20% deviation
// Each region should have roughly equal distribution
for (const count of Object.values(regionCounts)) {
expect(count).toBeGreaterThan(expectedCountPerRegion - tolerance)
expect(count).toBeLessThan(expectedCountPerRegion + tolerance)
}
})
it('should handle empty string', async () => {
const region = await selectBedrockRegion('')
const validRegions = Object.keys(bedrockRegionMap)
expect(validRegions).toContain(region)
})
it('should handle special characters in routing key', async () => {
const region = await selectBedrockRegion('key-with-special-chars!@#$%')
const validRegions = Object.keys(bedrockRegionMap)
expect(validRegions).toContain(region)
})
it('should return consistent results for unicode characters', async () => {
const routingKey = '🔑-unicode-key-测试'
const region1 = await selectBedrockRegion(routingKey)
const region2 = await selectBedrockRegion(routingKey)
expect(region1).toBe(region2)
})
})

View File

@@ -1,12 +1,11 @@
import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock'
import { createCredentialChain, fromNodeProviderChain } from '@aws-sdk/credential-providers'
import { CredentialsProviderError } from '@smithy/property-provider'
import { awsCredentialsProvider } from '@vercel/functions/oidc'
const credentialProvider = createCredentialChain(
// Vercel OIDC provider will be used for staging/production
awsCredentialsProvider({
roleArn: process.env.AWS_BEDROCK_ROLE_ARN!,
}),
vercelOidcProvider,
// AWS profile will be used for local development
fromNodeProviderChain({
@@ -14,10 +13,61 @@ const credentialProvider = createCredentialChain(
})
)
export const bedrock = createAmazonBedrock({
credentialProvider,
region: process.env.AWS_BEDROCK_REGION,
})
/**
* Creates a Vercel OIDC provider for AWS credentials.
*
* Wraps `awsCredentialsProvider` to properly handle errors
* so that it can be used in a credential chain.
*/
async function vercelOidcProvider() {
try {
return await awsCredentialsProvider({
roleArn: process.env.AWS_BEDROCK_ROLE_ARN!,
})()
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to create Vercel OIDC provider'
// Re-throw using the correct error type and `tryNextLink` option
throw new CredentialsProviderError(message, {
tryNextLink: true,
})
}
}
export const bedrockRegionMap = {
us: 'us-east-1',
eu: 'eu-central-1',
apac: 'ap-southeast-1',
} as const
export type BedrockRegion = keyof typeof bedrockRegionMap
export const bedrockForRegion = (region: BedrockRegion) =>
createAmazonBedrock({
credentialProvider,
region: bedrockRegionMap[region],
})
/**
* Selects a region based on a routing key using a consistent hashing algorithm.
*
* Ensures that the same key always maps to the same region
* while distributing keys evenly across available regions.
*/
export async function selectBedrockRegion(routingKey: string) {
const regions = Object.keys(bedrockRegionMap) as BedrockRegion[]
const encoder = new TextEncoder()
const data = encoder.encode(routingKey)
const hashBuffer = await crypto.subtle.digest('SHA-256', data)
// Use first 4 bytes (32 bit integer)
const hashInt = new DataView(hashBuffer).getUint32(0)
// Use modulo to map to available regions
const regionIndex = hashInt % regions.length
return regions[regionIndex]
}
export async function checkAwsCredentials() {
try {

View File

@@ -1,7 +1,6 @@
import { openai } from '@ai-sdk/openai'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import * as bedrockModule from './bedrock'
import { bedrock } from './bedrock'
import { getModel, ModelErrorMessage, modelsByProvider } from './model'
vi.mock('@ai-sdk/openai', () => ({
@@ -9,8 +8,9 @@ vi.mock('@ai-sdk/openai', () => ({
}))
vi.mock('./bedrock', () => ({
bedrock: vi.fn(() => 'bedrock-model'),
bedrockForRegion: vi.fn(() => () => 'bedrock-model'),
checkAwsCredentials: vi.fn(),
selectBedrockRegion: vi.fn(() => 'us'),
}))
describe('getModel', () => {
@@ -24,31 +24,23 @@ describe('getModel', () => {
process.env = { ...originalEnv }
})
it('should return bedrock model when AWS credentials are available and AWS_BEDROCK_REGION is set', async () => {
it('should return bedrock model when AWS credentials are available', async () => {
vi.mocked(bedrockModule.checkAwsCredentials).mockResolvedValue(true)
process.env.AWS_BEDROCK_REGION = 'us-east-1'
const { model, error } = await getModel()
console.log('Model:', model)
expect(model).toEqual('bedrock-model')
expect(bedrock).toHaveBeenCalledWith(modelsByProvider.bedrock)
expect(bedrockModule.bedrockForRegion).toHaveBeenCalledWith('us')
expect(error).toBeUndefined()
})
it('should return error when AWS credentials are available but AWS_BEDROCK_REGION is not set', async () => {
vi.mocked(bedrockModule.checkAwsCredentials).mockResolvedValue(true)
delete process.env.AWS_BEDROCK_REGION
const { error } = await getModel()
expect(error).toEqual(new Error('AWS_BEDROCK_REGION is not set'))
})
it('should return OpenAI model when AWS credentials are not available but OPENAI_API_KEY is set', async () => {
vi.mocked(bedrockModule.checkAwsCredentials).mockResolvedValue(false)
process.env.OPENAI_API_KEY = 'test-key'
const { model } = await getModel()
const { model } = await getModel('test-key')
expect(model).toEqual('openai-model')
expect(openai).toHaveBeenCalledWith(modelsByProvider.openai)
@@ -58,7 +50,7 @@ describe('getModel', () => {
vi.mocked(bedrockModule.checkAwsCredentials).mockResolvedValue(false)
delete process.env.OPENAI_API_KEY
const { error } = await getModel()
const { error } = await getModel('test-key')
expect(error).toEqual(new Error(ModelErrorMessage))
})

View File

@@ -1,9 +1,18 @@
import { openai } from '@ai-sdk/openai'
import { LanguageModel } from 'ai'
import { bedrock, checkAwsCredentials } from './bedrock'
import {
bedrockForRegion,
BedrockRegion,
checkAwsCredentials,
selectBedrockRegion,
} from './bedrock'
export const modelsByProvider = {
bedrock: 'us.anthropic.claude-3-7-sonnet-20250219-v1:0',
bedrock: {
us: 'us.anthropic.claude-3-7-sonnet-20250219-v1:0',
eu: 'eu.anthropic.claude-3-7-sonnet-20250219-v1:0',
apac: 'apac.anthropic.claude-3-7-sonnet-20250219-v1:0',
},
openai: 'gpt-4.1-2025-04-14',
}
@@ -24,20 +33,22 @@ export const ModelErrorMessage =
/**
* Retrieves the appropriate AI model based on available credentials.
*
* An optional routing key can be provided to distribute requests across
* different Bedrock regions.
*/
export async function getModel(): Promise<ModelResponse> {
export async function getModel(routingKey?: string): Promise<ModelResponse> {
const hasAwsCredentials = await checkAwsCredentials()
const hasOpenAIKey = !!process.env.OPENAI_API_KEY
if (hasAwsCredentials) {
if (!process.env.AWS_BEDROCK_REGION) {
return {
error: new Error('AWS_BEDROCK_REGION is not set'),
}
}
// Select the Bedrock region based on the routing key
const bedrockRegion: BedrockRegion = routingKey ? await selectBedrockRegion(routingKey) : 'us'
const bedrock = bedrockForRegion(bedrockRegion)
const modelName = modelsByProvider.bedrock[bedrockRegion]
return {
model: bedrock(modelsByProvider.bedrock),
model: bedrock(modelName),
}
}

View File

@@ -149,6 +149,7 @@
"@graphql-codegen/cli": "5.0.5",
"@graphql-typed-document-node/core": "^3.2.0",
"@radix-ui/react-use-escape-keydown": "^1.0.3",
"@smithy/property-provider": "^4.0.4",
"@supabase/postgres-meta": "^0.64.4",
"@tailwindcss/container-queries": "^0.1.1",
"@testing-library/dom": "^10.0.0",

View File

@@ -38,12 +38,6 @@ export default wrapper
async function handlePost(req: NextApiRequest, res: NextApiResponse) {
try {
const { model, error: modelError } = await getModel()
if (modelError) {
return res.status(500).json({ error: modelError.message })
}
const { completionMetadata, projectRef, connectionString, includeSchemaMetadata } = req.body
const { textBeforeCursor, textAfterCursor, language, prompt, selection } = completionMetadata
@@ -53,6 +47,12 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse) {
})
}
const { model, error: modelError } = await getModel(projectRef)
if (modelError) {
return res.status(500).json({ error: modelError.message })
}
const authorization = req.headers.authorization
const { result: schemas } = includeSchemaMetadata

View File

@@ -26,12 +26,6 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
}
try {
const { model, error: modelError } = await getModel()
if (modelError) {
return res.status(500).json({ error: modelError.message })
}
const { completionMetadata, projectRef, connectionString, includeSchemaMetadata } = req.body
const { textBeforeCursor, textAfterCursor, language, prompt, selection } = completionMetadata
@@ -40,6 +34,11 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
error: 'Missing project_ref in request body',
})
}
const { model, error: modelError } = await getModel(projectRef)
if (modelError) {
return res.status(500).json({ error: modelError.message })
}
const authorization = req.headers.authorization

View File

@@ -59,12 +59,6 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse) {
return res.status(401).json({ error: 'Authorization token is required' })
}
const { model, error: modelError } = await getModel()
if (modelError) {
return res.status(500).json({ error: modelError.message })
}
const body = typeof req.body === 'string' ? JSON.parse(req.body) : req.body
const { data, error: parseError } = requestBodySchema.safeParse(body)
@@ -74,6 +68,12 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse) {
const { messages, projectRef, connectionString, aiOptInLevel } = data
const { model, error: modelError } = await getModel(projectRef) // use project ref as routing key
if (modelError) {
return res.status(500).json({ error: modelError.message })
}
try {
let mcpTools: ToolSet = {}
let localTools: ToolSet = {

44
pnpm-lock.yaml generated
View File

@@ -331,7 +331,7 @@ importers:
version: 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@sentry/nextjs':
specifier: ^9.15.0
version: 9.15.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0)(supports-color@8.1.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1)(supports-color@8.1.1)(webpack@5.94.0)
version: 9.15.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0)(supports-color@8.1.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1)(supports-color@8.1.1)(webpack@5.94.0)
'@supabase/supabase-js':
specifier: 'catalog:'
version: 2.49.3
@@ -451,7 +451,7 @@ importers:
version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
nuqs:
specifier: ^1.19.1
version: 1.19.1(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))
version: 1.19.1(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))
openai:
specifier: ^4.20.1
version: 4.71.1(encoding@0.1.13)(zod@3.23.8)
@@ -1018,6 +1018,9 @@ importers:
'@radix-ui/react-use-escape-keydown':
specifier: ^1.0.3
version: 1.1.0(@types/react@18.3.3)(react@18.3.1)
'@smithy/property-provider':
specifier: ^4.0.4
version: 4.0.4
'@supabase/postgres-meta':
specifier: ^0.64.4
version: 0.64.6(encoding@0.1.13)(supports-color@8.1.1)
@@ -1134,7 +1137,7 @@ importers:
version: 2.4.11(typescript@5.5.2)
next-router-mock:
specifier: ^0.9.13
version: 0.9.13(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1)
version: 0.9.13(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1)
postcss:
specifier: ^8.5.3
version: 8.5.3
@@ -2290,7 +2293,7 @@ importers:
version: link:../api-types
next-router-mock:
specifier: ^0.9.13
version: 0.9.13(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1)
version: 0.9.13(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1)
tsx:
specifier: ^4.19.3
version: 4.19.3
@@ -2751,6 +2754,10 @@ packages:
resolution: {integrity: sha512-Znroqdai1a90TlxGaJ+FK1lwC0fHpo97Xjsp5UKGR5JODYm7f9+/fF17ebO1KdoBr/Rm0UIFiF5VmI8ts9F1eA==}
engines: {node: '>=18.0.0'}
'@aws-sdk/types@3.840.0':
resolution: {integrity: sha512-xliuHaUFZxEx1NSXeLLZ9Dyu6+EJVQKEoD+yM+zqUo3YDZ7medKJWY6fIOKiPX/N7XbLdBYwajb15Q7IL8KkeA==}
engines: {node: '>=18.0.0'}
'@aws-sdk/util-arn-parser@3.804.0':
resolution: {integrity: sha512-wmBJqn1DRXnZu3b4EkE6CWnoWMo1ZMvlfkqU5zPz67xx1GMaXlDCchFvKAXMjk4jn/L1O3tKnoFDNsoLV1kgNQ==}
engines: {node: '>=18.0.0'}
@@ -11104,7 +11111,6 @@ packages:
resolution: {integrity: sha512-t0q23FIpvHDTtnORW+bDJziGsal5uh9RJTJ1fyH8drd4lICOoXhJ5pLMUZ5C0VQei6dNmwTzzoTRgMkO9JgHEQ==}
peerDependencies:
eslint: '>= 5'
bundledDependencies: []
eslint-plugin-import@2.31.0:
resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==}
@@ -14174,6 +14180,7 @@ packages:
node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'}
deprecated: Use your platform's native DOMException instead
node-emoji@1.11.0:
resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==}
@@ -18278,7 +18285,7 @@ snapshots:
'@aws-crypto/crc32@5.2.0':
dependencies:
'@aws-crypto/util': 5.2.0
'@aws-sdk/types': 3.468.0
'@aws-sdk/types': 3.840.0
tslib: 2.8.1
'@aws-crypto/crc32c@5.2.0':
@@ -18306,7 +18313,7 @@ snapshots:
'@aws-crypto/sha256-js': 3.0.0
'@aws-crypto/supports-web-crypto': 3.0.0
'@aws-crypto/util': 3.0.0
'@aws-sdk/types': 3.468.0
'@aws-sdk/types': 3.821.0
'@aws-sdk/util-locate-window': 3.465.0
'@aws-sdk/util-utf8-browser': 3.259.0
tslib: 1.14.1
@@ -18316,7 +18323,7 @@ snapshots:
'@aws-crypto/sha256-js': 5.2.0
'@aws-crypto/supports-web-crypto': 5.2.0
'@aws-crypto/util': 5.2.0
'@aws-sdk/types': 3.821.0
'@aws-sdk/types': 3.840.0
'@aws-sdk/util-locate-window': 3.465.0
'@smithy/util-utf8': 2.0.2
tslib: 2.8.1
@@ -18324,19 +18331,19 @@ snapshots:
'@aws-crypto/sha256-js@1.2.2':
dependencies:
'@aws-crypto/util': 1.2.2
'@aws-sdk/types': 3.468.0
'@aws-sdk/types': 3.821.0
tslib: 1.14.1
'@aws-crypto/sha256-js@3.0.0':
dependencies:
'@aws-crypto/util': 3.0.0
'@aws-sdk/types': 3.468.0
'@aws-sdk/types': 3.821.0
tslib: 1.14.1
'@aws-crypto/sha256-js@5.2.0':
dependencies:
'@aws-crypto/util': 5.2.0
'@aws-sdk/types': 3.821.0
'@aws-sdk/types': 3.840.0
tslib: 2.8.1
'@aws-crypto/supports-web-crypto@3.0.0':
@@ -18361,7 +18368,7 @@ snapshots:
'@aws-crypto/util@5.2.0':
dependencies:
'@aws-sdk/types': 3.821.0
'@aws-sdk/types': 3.840.0
'@smithy/util-utf8': 2.0.2
tslib: 2.8.1
@@ -19440,6 +19447,11 @@ snapshots:
'@smithy/types': 4.3.1
tslib: 2.8.1
'@aws-sdk/types@3.840.0':
dependencies:
'@smithy/types': 4.3.1
tslib: 2.8.1
'@aws-sdk/util-arn-parser@3.804.0':
dependencies:
tslib: 2.8.1
@@ -25756,7 +25768,7 @@ snapshots:
- supports-color
- webpack
'@sentry/nextjs@9.15.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0)(supports-color@8.1.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1)(supports-color@8.1.1)(webpack@5.94.0)':
'@sentry/nextjs@9.15.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0)(supports-color@8.1.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1)(supports-color@8.1.1)(webpack@5.94.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/semantic-conventions': 1.32.0
@@ -30370,7 +30382,7 @@ snapshots:
es-set-tostringtag@2.1.0:
dependencies:
es-errors: 1.3.0
get-intrinsic: 1.2.7
get-intrinsic: 1.3.0
has-tostringtag: 1.0.2
hasown: 2.0.2
@@ -34690,7 +34702,7 @@ snapshots:
dependencies:
js-yaml-loader: 1.2.2
next-router-mock@0.9.13(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1):
next-router-mock@0.9.13(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1):
dependencies:
next: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)
react: 18.3.1
@@ -35027,7 +35039,7 @@ snapshots:
number-flow@0.3.7: {}
nuqs@1.19.1(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)):
nuqs@1.19.1(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)):
dependencies:
mitt: 3.0.1
next: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)

View File

@@ -89,7 +89,6 @@
"SENTRY_ORG",
"SENTRY_PROJECT",
"SENTRY_AUTH_TOKEN",
"AWS_BEDROCK_REGION",
"AWS_BEDROCK_PROFILE",
"AWS_BEDROCK_ROLE_ARN",
"AWS_ACCESS_KEY_ID",