Reflect Cross-region inference option in ap-xx region (#1842)

* Fix: Enable cross-region inference for 'ap-xx' region in AwsBedrockHandler.completePrompt

* Fix: Enable cross-region inference for 'ap-xx' region in AwsBedrockHandler.createMessage

* Create itchy-waves-move.md

---------

Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com>
This commit is contained in:
Yukky 2025-03-21 05:03:51 +09:00 committed by GitHub
parent 842e69ceb8
commit b033082523
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 327 additions and 1 deletions

View file

@ -0,0 +1,5 @@
---
"roo-cline": patch
---
Reflect Cross-region inference option in `ap-xx` region

View file

@ -165,6 +165,222 @@ describe("AwsBedrockHandler", () => {
)
})
it("should handle cross-region inference for us-xx region", async () => {
const handlerWithProfile = new AwsBedrockHandler({
apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
awsAccessKey: "test-access-key",
awsSecretKey: "test-secret-key",
awsRegion: "us-east-1",
awsUseCrossRegionInference: true,
})
// Mock AWS SDK invoke
const mockStream = {
[Symbol.asyncIterator]: async function* () {
yield {
metadata: {
usage: {
inputTokens: 10,
outputTokens: 5,
},
},
}
},
}
const mockInvoke = jest.fn().mockResolvedValue({
stream: mockStream,
})
handlerWithProfile["client"] = {
send: mockInvoke,
} as unknown as BedrockRuntimeClient
const stream = handlerWithProfile.createMessage(systemPrompt, mockMessages)
const chunks = []
for await (const chunk of stream) {
chunks.push(chunk)
}
expect(chunks.length).toBeGreaterThan(0)
expect(chunks[0]).toEqual({
type: "usage",
inputTokens: 10,
outputTokens: 5,
})
expect(mockInvoke).toHaveBeenCalledWith(
expect.objectContaining({
input: expect.objectContaining({
modelId: "us.anthropic.claude-3-5-sonnet-20241022-v2:0",
}),
}),
)
})
it("should handle cross-region inference for eu-xx region", async () => {
const handlerWithProfile = new AwsBedrockHandler({
apiModelId: "anthropic.claude-3-5-sonnet-20240620-v1:0",
awsAccessKey: "test-access-key",
awsSecretKey: "test-secret-key",
awsRegion: "eu-west-1",
awsUseCrossRegionInference: true,
})
// Mock AWS SDK invoke
const mockStream = {
[Symbol.asyncIterator]: async function* () {
yield {
metadata: {
usage: {
inputTokens: 10,
outputTokens: 5,
},
},
}
},
}
const mockInvoke = jest.fn().mockResolvedValue({
stream: mockStream,
})
handlerWithProfile["client"] = {
send: mockInvoke,
} as unknown as BedrockRuntimeClient
const stream = handlerWithProfile.createMessage(systemPrompt, mockMessages)
const chunks = []
for await (const chunk of stream) {
chunks.push(chunk)
}
expect(chunks.length).toBeGreaterThan(0)
expect(chunks[0]).toEqual({
type: "usage",
inputTokens: 10,
outputTokens: 5,
})
expect(mockInvoke).toHaveBeenCalledWith(
expect.objectContaining({
input: expect.objectContaining({
modelId: "eu.anthropic.claude-3-5-sonnet-20240620-v1:0",
}),
}),
)
})
it("should handle cross-region inference for ap-xx region", async () => {
const handlerWithProfile = new AwsBedrockHandler({
apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
awsAccessKey: "test-access-key",
awsSecretKey: "test-secret-key",
awsRegion: "ap-northeast-1",
awsUseCrossRegionInference: true,
})
// Mock AWS SDK invoke
const mockStream = {
[Symbol.asyncIterator]: async function* () {
yield {
metadata: {
usage: {
inputTokens: 10,
outputTokens: 5,
},
},
}
},
}
const mockInvoke = jest.fn().mockResolvedValue({
stream: mockStream,
})
handlerWithProfile["client"] = {
send: mockInvoke,
} as unknown as BedrockRuntimeClient
const stream = handlerWithProfile.createMessage(systemPrompt, mockMessages)
const chunks = []
for await (const chunk of stream) {
chunks.push(chunk)
}
expect(chunks.length).toBeGreaterThan(0)
expect(chunks[0]).toEqual({
type: "usage",
inputTokens: 10,
outputTokens: 5,
})
expect(mockInvoke).toHaveBeenCalledWith(
expect.objectContaining({
input: expect.objectContaining({
modelId: "apac.anthropic.claude-3-5-sonnet-20241022-v2:0",
}),
}),
)
})
it("should handle cross-region inference for other region", async () => {
const handlerWithProfile = new AwsBedrockHandler({
apiModelId: "anthropic.claude-3-sonnet-20240229-v1:0",
awsAccessKey: "test-access-key",
awsSecretKey: "test-secret-key",
awsRegion: "ca-central-1",
awsUseCrossRegionInference: true,
})
// Mock AWS SDK invoke
const mockStream = {
[Symbol.asyncIterator]: async function* () {
yield {
metadata: {
usage: {
inputTokens: 10,
outputTokens: 5,
},
},
}
},
}
const mockInvoke = jest.fn().mockResolvedValue({
stream: mockStream,
})
handlerWithProfile["client"] = {
send: mockInvoke,
} as unknown as BedrockRuntimeClient
const stream = handlerWithProfile.createMessage(systemPrompt, mockMessages)
const chunks = []
for await (const chunk of stream) {
chunks.push(chunk)
}
expect(chunks.length).toBeGreaterThan(0)
expect(chunks[0]).toEqual({
type: "usage",
inputTokens: 10,
outputTokens: 5,
})
expect(mockInvoke).toHaveBeenCalledWith(
expect.objectContaining({
input: expect.objectContaining({
modelId: "anthropic.claude-3-sonnet-20240229-v1:0",
}),
}),
)
})
it("should handle API errors", async () => {
// Mock AWS SDK invoke with error
const mockInvoke = jest.fn().mockRejectedValue(new Error("AWS Bedrock error"))
@ -260,7 +476,7 @@ describe("AwsBedrockHandler", () => {
expect(result).toBe("")
})
it("should handle cross-region inference", async () => {
it("should handle cross-region inference for us-xx region", async () => {
handler = new AwsBedrockHandler({
apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
awsAccessKey: "test-access-key",
@ -292,6 +508,105 @@ describe("AwsBedrockHandler", () => {
}),
)
})
it("should handle cross-region inference for eu-xx region", async () => {
handler = new AwsBedrockHandler({
apiModelId: "anthropic.claude-3-5-sonnet-20240620-v1:0",
awsAccessKey: "test-access-key",
awsSecretKey: "test-secret-key",
awsRegion: "eu-west-1",
awsUseCrossRegionInference: true,
})
const mockResponse = {
output: new TextEncoder().encode(
JSON.stringify({
content: "Test response",
}),
),
}
const mockSend = jest.fn().mockResolvedValue(mockResponse)
handler["client"] = {
send: mockSend,
} as unknown as BedrockRuntimeClient
const result = await handler.completePrompt("Test prompt")
expect(result).toBe("Test response")
expect(mockSend).toHaveBeenCalledWith(
expect.objectContaining({
input: expect.objectContaining({
modelId: "eu.anthropic.claude-3-5-sonnet-20240620-v1:0",
}),
}),
)
})
it("should handle cross-region inference for ap-xx region", async () => {
handler = new AwsBedrockHandler({
apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
awsAccessKey: "test-access-key",
awsSecretKey: "test-secret-key",
awsRegion: "ap-northeast-1",
awsUseCrossRegionInference: true,
})
const mockResponse = {
output: new TextEncoder().encode(
JSON.stringify({
content: "Test response",
}),
),
}
const mockSend = jest.fn().mockResolvedValue(mockResponse)
handler["client"] = {
send: mockSend,
} as unknown as BedrockRuntimeClient
const result = await handler.completePrompt("Test prompt")
expect(result).toBe("Test response")
expect(mockSend).toHaveBeenCalledWith(
expect.objectContaining({
input: expect.objectContaining({
modelId: "apac.anthropic.claude-3-5-sonnet-20241022-v2:0",
}),
}),
)
})
it("should handle cross-region inference for other regions", async () => {
handler = new AwsBedrockHandler({
apiModelId: "anthropic.claude-3-sonnet-20240229-v1:0",
awsAccessKey: "test-access-key",
awsSecretKey: "test-secret-key",
awsRegion: "ca-central-1",
awsUseCrossRegionInference: true,
})
const mockResponse = {
output: new TextEncoder().encode(
JSON.stringify({
content: "Test response",
}),
),
}
const mockSend = jest.fn().mockResolvedValue(mockResponse)
handler["client"] = {
send: mockSend,
} as unknown as BedrockRuntimeClient
const result = await handler.completePrompt("Test prompt")
expect(result).toBe("Test response")
expect(mockSend).toHaveBeenCalledWith(
expect.objectContaining({
input: expect.objectContaining({
modelId: "anthropic.claude-3-sonnet-20240229-v1:0",
}),
}),
)
})
})
describe("getModel", () => {

View file

@ -211,6 +211,9 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
case "eu-":
modelId = `eu.${modelConfig.id}`
break
case "ap-":
modelId = `apac.${modelConfig.id}`
break
default:
modelId = modelConfig.id
break
@ -610,6 +613,9 @@ Please check:
case "eu-":
modelId = `eu.${modelConfig.id}`
break
case "ap-":
modelId = `apac.${modelConfig.id}`
break
default:
modelId = modelConfig.id
break