Skip to content
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

feat:Add Amazon Bedrock to support the leading large language models such as Amazon Nova, Claude, Llama, Mistral, and others. #5746

Open
wants to merge 90 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 79 commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
ff356f0
修改: app/api/[provider]/[...path]/route.ts
glayyiyi Oct 29, 2024
722c288
Merge branch 'ChatGPTNextWeb:main' into main
glayyiyi Oct 31, 2024
dca4a0e
修改: app/api/bedrock.ts
glayyiyi Oct 31, 2024
fc39116
修改: app/api/bedrock.ts
glayyiyi Nov 4, 2024
0f276f5
修改: app/client/platforms/bedrock.ts
glayyiyi Nov 5, 2024
afbf5eb
修改: .env.template
glayyiyi Nov 5, 2024
58837f6
修改: app/api/bedrock.ts
glayyiyi Nov 5, 2024
f532731
修改: app/client/platforms/bedrock.ts
glayyiyi Nov 5, 2024
e3c18bb
修改: app/api/bedrock.ts
glayyiyi Nov 5, 2024
d55c752
修改: app/api/bedrock.ts
glayyiyi Nov 5, 2024
1164e1b
Merge feature/update-bedrock-api into main
glayyiyi Nov 5, 2024
1998cf5
Merge feature/update-bedrock-api into main
glayyiyi Nov 5, 2024
045adc3
修改: app/api/bedrock.ts
glayyiyi Nov 5, 2024
1f66d37
修改: app/api/bedrock.ts
glayyiyi Nov 5, 2024
cae20af
修改: app/api/bedrock.ts
glayyiyi Nov 5, 2024
ca17e90
修改: app/utils/encryption.ts
glayyiyi Nov 5, 2024
c55cea5
修改: app/store/access.ts
glayyiyi Nov 6, 2024
952d883
修改: app/components/settings.tsx
glayyiyi Nov 6, 2024
3bf55d3
Merge branch 'main' into main
glayyiyi Nov 6, 2024
f0c23cc
修改: app/store/access.ts
glayyiyi Nov 6, 2024
5d5456c
修改: app/api/bedrock.ts
glayyiyi Nov 6, 2024
4204890
Merge branch 'ChatGPTNextWeb:main' into main
glayyiyi Nov 6, 2024
9e04198
Merge branch 'main' into main
glayyiyi Nov 6, 2024
82a368a
修改: .env.template
glayyiyi Nov 7, 2024
0e09697
Merge branch 'ChatGPTNextWeb:main' into main
glayyiyi Nov 8, 2024
09e4f95
Merge branch 'main' into main
glayyiyi Nov 11, 2024
f120584
Merge branch 'main' into main
glayyiyi Nov 12, 2024
afb0752
Merge branch 'ChatGPTNextWeb:main' into main
glayyiyi Nov 12, 2024
bfa4339
Add AWS access key validation.
glayyiyi Nov 12, 2024
70f066c
Add AWS region validation and improve code style.
glayyiyi Nov 13, 2024
1b5a81c
Add AWS secret key validation.
glayyiyi Nov 13, 2024
24261d2
Consider adding more Bedrock-specific configurations
glayyiyi Nov 13, 2024
6bc1612
修改: app/locales/cn.ts
glayyiyi Nov 13, 2024
225ad30
修改: app/api/bedrock.ts
glayyiyi Nov 13, 2024
b2d5e0e
Merge branch 'ChatGPTNextWeb:main' into main
glayyiyi Nov 13, 2024
dfeb9e7
Merge branch 'ChatGPTNextWeb:main' into main
glayyiyi Nov 17, 2024
9d3f1d2
remove document function,only keep the bedrock service provider
glayyiyi Nov 18, 2024
f60c237
去掉sdk的引入,客户端也能直连
glayyiyi Nov 20, 2024
bd68df1
修改: app/api/bedrock.ts
glayyiyi Nov 21, 2024
b0c1ccd
优化和重构代码,增加前端可以设置加密配置数据的密钥
glayyiyi Nov 22, 2024
a85db21
优化代码,修改方法命名错误
glayyiyi Nov 23, 2024
ff88421
修改密钥加密逻辑
glayyiyi Nov 23, 2024
a6337e9
完善总结功能的代码逻辑
glayyiyi Nov 23, 2024
238eb70
完善mistral模型的推理结果
glayyiyi Nov 23, 2024
513cf1b
完善llama和mistral模型的推理功能
glayyiyi Nov 23, 2024
a19ba69
Merge branch 'ChatGPTNextWeb:main' into main
glayyiyi Nov 24, 2024
2ccdd17
优化前后端代码,将公共方法抽取到util类,修改客户端加密方式
glayyiyi Nov 24, 2024
6f7a635
完善llama和mistral模型的推理功能
glayyiyi Nov 24, 2024
5bd7e28
去掉Debug日志打印
glayyiyi Nov 25, 2024
0abfd27
完善bedrock中文翻译
glayyiyi Nov 25, 2024
2fe848e
去掉Debug日志打印
glayyiyi Nov 25, 2024
9a47304
去掉Debug日志打印
glayyiyi Nov 25, 2024
15d0600
Merge branch 'ChatGPTNextWeb:main' into main
glayyiyi Nov 25, 2024
e663375
完善mistral tool use功能 和llama3消息格式问题
glayyiyi Nov 25, 2024
448babd
完善mistral tool use功能
glayyiyi Nov 26, 2024
b39b3f7
Remove detailed error logging in decryption function.
glayyiyi Nov 26, 2024
9c648e5
Remove detailed error error message.
glayyiyi Nov 26, 2024
8ce2cf5
Remove detailed error error message.
glayyiyi Nov 26, 2024
471b178
modify the BEDROCK_BASE_URL to use the region from the access stor
glayyiyi Nov 26, 2024
d9d2a27
modify the BEDROCK_BASE_URL to use the region from the access store
glayyiyi Nov 26, 2024
a75b9f7
Merge branch 'ChatGPTNextWeb:main' into main
glayyiyi Dec 4, 2024
0c55850
优化bedrock流式消息处理的缓冲机制,简化app和后台api调用逻辑判断和处理
glayyiyi Dec 7, 2024
4254fd3
增加bedrock最新nova模型,包括image解析的支持
glayyiyi Dec 7, 2024
57dc44a
增加bedrock最新nova模型,优化后台代码
glayyiyi Dec 7, 2024
ad49cd0
优化bedrock工具类,以更安全的处理流式消息的边界值
glayyiyi Dec 7, 2024
5ac651a
Enhance encryption security
glayyiyi Dec 7, 2024
603415f
Enhance log security
glayyiyi Dec 8, 2024
26b9fa9
优化代码
glayyiyi Dec 8, 2024
f5ae086
Enhance encryption security with additional safeguards.
glayyiyi Dec 8, 2024
fb3437c
Update app/client/platforms/bedrock.ts
glayyiyi Dec 8, 2024
7830b37
Enhance encryption security with additional safeguards.
glayyiyi Dec 8, 2024
4b2f447
Enhance encryption security with additional safeguards.
glayyiyi Dec 8, 2024
93337b2
Update app/client/platforms/bedrock.ts
glayyiyi Dec 8, 2024
a088687
Enhance encryption security with additional safeguards.
glayyiyi Dec 8, 2024
44a1cf6
Update app/client/platforms/bedrock.ts
glayyiyi Dec 9, 2024
50a241b
Update app/utils/aws.ts
glayyiyi Dec 9, 2024
372a327
Update app/utils/aws.ts
glayyiyi Dec 9, 2024
2a9f7d7
Update app/client/platforms/bedrock.ts
glayyiyi Dec 9, 2024
12d38aa
Update app/utils/aws.ts
glayyiyi Dec 9, 2024
19437c7
Enhance encryption security with additional safeguards.
glayyiyi Dec 10, 2024
e455840
Update app/utils/aws.ts
glayyiyi Dec 10, 2024
cb0422b
Enhance processChunks by attempting to recover by processing the next…
glayyiyi Dec 10, 2024
0ec1ae6
Enhance encryption security with additional safeguards.
glayyiyi Dec 10, 2024
e839940
feat:add amazon.nova model tool use support.
glayyiyi Dec 14, 2024
9643adc
Merge branch 'main' into main
glayyiyi Dec 21, 2024
92615da
Merge branch 'main' into main
glayyiyi Dec 22, 2024
26f79aa
feat:add nova VISION_MODEL_REGEXES
glayyiyi Dec 22, 2024
89b1774
Merge branch 'ChatGPTNextWeb:main' into main
glayyiyi Dec 25, 2024
29b9a20
feat:add meta.llama3-3-70b-instruct model
glayyiyi Dec 25, 2024
b0f78e9
Merge branch 'main' into main
glayyiyi Dec 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,9 @@ ANTHROPIC_API_VERSION=
ANTHROPIC_URL=

### (optional)
WHITE_WEBDAV_ENDPOINTS=
WHITE_WEBDAV_ENDPOINTS=

### bedrock (optional)
AWS_REGION=
AWS_ACCESS_KEY=AKIA
AWS_SECRET_KEY=
4 changes: 4 additions & 0 deletions app/api/[provider]/[...path]/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ApiPath } from "@/app/constant";
import { NextRequest } from "next/server";
import { handle as openaiHandler } from "../../openai";
import { handle as bedrockHandler } from "../../bedrock";
import { handle as azureHandler } from "../../azure";
import { handle as googleHandler } from "../../google";
import { handle as anthropicHandler } from "../../anthropic";
Expand All @@ -21,12 +22,15 @@ async function handle(
const apiPath = `/api/${params.provider}`;
console.log(`[${params.provider} Route] params `, params);
switch (apiPath) {
case ApiPath.Bedrock:
return bedrockHandler(req, { params });
case ApiPath.Azure:
return azureHandler(req, { params });
case ApiPath.Google:
return googleHandler(req, { params });
case ApiPath.Anthropic:
return anthropicHandler(req, { params });

case ApiPath.Baidu:
return baiduHandler(req, { params });
case ApiPath.ByteDance:
Expand Down
9 changes: 8 additions & 1 deletion app/api/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ export function auth(req: NextRequest, modelProvider: ModelProvider) {
msg: "you are not allowed to access with your own api key",
};
}

// if user does not provide an api key, inject system api key
if (!apiKey) {
const serverConfig = getServerSideConfig();
Expand Down Expand Up @@ -98,6 +97,14 @@ export function auth(req: NextRequest, modelProvider: ModelProvider) {
case ModelProvider.ChatGLM:
systemApiKey = serverConfig.chatglmApiKey;
break;
case ModelProvider.Bedrock:
systemApiKey =
serverConfig.awsRegion +
":" +
serverConfig.awsAccessKey +
":" +
serverConfig.awsSecretKey;
break;
case ModelProvider.GPT:
default:
if (req.nextUrl.pathname.includes("azure/deployments")) {
Expand Down
177 changes: 177 additions & 0 deletions app/api/bedrock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import { NextRequest, NextResponse } from "next/server";
import { auth } from "./auth";
import {
sign,
decrypt,
getBedrockEndpoint,
BedrockCredentials,
} from "../utils/aws";
import { getServerSideConfig } from "../config/server";
import { ModelProvider } from "../constant";
import { prettyObject } from "../utils/format";

const ALLOWED_PATH = new Set(["chat", "models"]);

async function getBedrockCredentials(
req: NextRequest,
): Promise<BedrockCredentials> {
// Get AWS credentials from server config first
const config = getServerSideConfig();
let awsRegion = config.awsRegion;
let awsAccessKey = config.awsAccessKey;
let awsSecretKey = config.awsSecretKey;
glayyiyi marked this conversation as resolved.
Show resolved Hide resolved

// If server-side credentials are not available, parse from Authorization header
if (!awsRegion || !awsAccessKey || !awsSecretKey) {
const authHeader = req.headers.get("Authorization");
if (!authHeader || !authHeader.startsWith("Bearer ")) {
throw new Error("Missing or invalid Authorization header");
}

const [_, credentials] = authHeader.split("Bearer ");
const [encryptedRegion, encryptedAccessKey, encryptedSecretKey] =
credentials.split(":");

if (!encryptedRegion || !encryptedAccessKey || !encryptedSecretKey) {
throw new Error("Invalid Authorization header format");
}
const encryptionKey = req.headers.get("XEncryptionKey") || "";
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Security concern: Encryption key transmission

Transmitting the encryption key via headers (XEncryptionKey) is insecure as it could be intercepted.

Consider implementing server-side encryption key management or using AWS's secure credential management solutions like AWS Secrets Manager or AWS KMS.

// Decrypt the credentials
Comment on lines +38 to +39
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Security concern: Encryption key transmission

The encryption key is transmitted via headers (XEncryptionKey), which could be intercepted. Consider implementing a more secure key transmission mechanism or server-side encryption.

Also applies to: 40-42

[awsRegion, awsAccessKey, awsSecretKey] = await Promise.all([
decrypt(encryptedRegion, encryptionKey),
decrypt(encryptedAccessKey, encryptionKey),
decrypt(encryptedSecretKey, encryptionKey),
]);

if (!awsRegion || !awsAccessKey || !awsSecretKey) {
throw new Error(
"Failed to decrypt AWS credentials. Please ensure ENCRYPTION_KEY is set correctly.",
);
}
glayyiyi marked this conversation as resolved.
Show resolved Hide resolved
Comment on lines +46 to +50
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid exposing sensitive information in error messages

Error messages contain details about encryption mechanism and configuration.

Apply this diff to provide generic error messages:

-      throw new Error(
-        "Failed to decrypt AWS credentials. Please ensure ENCRYPTION_KEY is set correctly.",
-      );
+      throw new Error("Invalid or expired credentials");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!awsRegion || !awsAccessKey || !awsSecretKey) {
throw new Error(
"Failed to decrypt AWS credentials. Please ensure ENCRYPTION_KEY is set correctly.",
);
}
if (!awsRegion || !awsAccessKey || !awsSecretKey) {
throw new Error("Invalid or expired credentials");
}

}

return {
region: awsRegion,
accessKeyId: awsAccessKey,
secretAccessKey: awsSecretKey,
};
}

async function requestBedrock(req: NextRequest) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10 * 60 * 1000);
glayyiyi marked this conversation as resolved.
Show resolved Hide resolved

try {
// Get credentials and model info
const credentials = await getBedrockCredentials(req);
const modelId = req.headers.get("XModelID");
const shouldStream = req.headers.get("ShouldStream") !== "false";

if (!modelId) {
throw new Error("Missing model ID");
}

// Parse and validate request body
const bodyText = await req.clone().text();
if (!bodyText) {
throw new Error("Request body is empty");
}
let bodyJson;
try {
bodyJson = JSON.parse(bodyText);
} catch (e) {
throw new Error(`Invalid JSON in request body: ${e}`);
}
console.log("[Bedrock Request] Initiating request");
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove sensitive request logging

Logging request details could expose sensitive information.

Apply this diff:

-    console.log("[Bedrock Request] Initiating request");
+    console.log("[Bedrock] Processing request");

-    // console.log(
-    //   "[Bedrock Request] Final Body:",
-    //   JSON.stringify(requestBody, null, 2),
-    // );

Also applies to: 106-109

// Get endpoint and prepare request
const endpoint = getBedrockEndpoint(
credentials.region,
modelId,
shouldStream,
);
const requestBody: any = {
...bodyJson,
};
// Sign request
Comment on lines +92 to +95
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Replace 'any' with a specific type

Using 'any' type reduces type safety.

-    const requestBody: any = {
+    const requestBody: {
+      input?: string;
+      max_tokens?: number;
+      temperature?: number;
+      top_p?: number;
+      stop_sequences?: string[];
+      anthropic_version?: string;
+      messages?: Array<{
+        role: string;
+        content: string;
+      }>;
+    } = {
       ...bodyJson,
     };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const requestBody: any = {
...bodyJson,
};
// Sign request
const requestBody: {
input?: string;
max_tokens?: number;
temperature?: number;
top_p?: number;
stop_sequences?: string[];
anthropic_version?: string;
messages?: Array<{
role: string;
content: string;
}>;
} = {
...bodyJson,
};
// Sign request

const headers = await sign({
method: "POST",
url: endpoint,
region: credentials.region,
accessKeyId: credentials.accessKeyId,
secretAccessKey: credentials.secretAccessKey,
body: JSON.stringify(requestBody),
service: "bedrock",
isStreaming: shouldStream,
});

// Make request to AWS Bedrock
// console.log(
// "[Bedrock Request] Final Body:",
// JSON.stringify(requestBody, null, 2),
// );
const res = await fetch(endpoint, {
method: "POST",
headers,
body: JSON.stringify(requestBody),
redirect: "manual",
// @ts-ignore
duplex: "half",
signal: controller.signal,
});

if (!res.ok) {
const error = await res.text();
console.error("[Bedrock Error] Request failed with status:", res.status);
try {
const errorJson = JSON.parse(error);
throw new Error(errorJson.message || error);
} catch {
throw new Error(
`Bedrock request failed with status ${res.status}: ${
error || "No error message"
}`,
);
}
}
Comment on lines +122 to +135
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Sanitize error responses

Exposing detailed error messages could reveal sensitive information about the system.

Apply this diff:

   if (!res.ok) {
     const error = await res.text();
-    console.error("[Bedrock Error] Request failed with status:", res.status);
+    console.error("[Bedrock Error] Request failed");
     try {
       const errorJson = JSON.parse(error);
-      throw new Error(errorJson.message || error);
+      throw new Error("Request to Bedrock failed");
     } catch {
-      throw new Error(
-        `Bedrock request failed with status ${res.status}: ${
-          error || "No error message"
-        }`,
-      );
+      throw new Error("Request to Bedrock failed");
     }
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!res.ok) {
const error = await res.text();
console.error("[Bedrock Error] Request failed with status:", res.status);
try {
const errorJson = JSON.parse(error);
throw new Error(errorJson.message || error);
} catch {
throw new Error(
`Bedrock request failed with status ${res.status}: ${
error || "No error message"
}`,
);
}
}
if (!res.ok) {
const error = await res.text();
console.error("[Bedrock Error] Request failed");
try {
const errorJson = JSON.parse(error);
throw new Error("Request to Bedrock failed");
} catch {
throw new Error("Request to Bedrock failed");
}
}


if (!res.body) {
console.error("[Bedrock Error] Empty response body");
throw new Error(
"Empty response from Bedrock. Please check AWS credentials and permissions.",
);
}
return res;
} catch (e) {
console.error("[Bedrock Request Error]:", e);
throw e;
} finally {
clearTimeout(timeoutId);
}
}
glayyiyi marked this conversation as resolved.
Show resolved Hide resolved
glayyiyi marked this conversation as resolved.
Show resolved Hide resolved

export async function handle(
req: NextRequest,
{ params }: { params: { path: string[] } },
) {
const subpath = params.path.join("/");
if (!ALLOWED_PATH.has(subpath)) {
return NextResponse.json(
{ error: true, msg: "you are not allowed to request " + subpath },
{ status: 403 },
);
Comment on lines +156 to +161
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve path validation

The current path validation is basic and could be bypassed.

+  const VALID_PATHS = new Set(["chat", "models"]);
+  
+  if (!params?.path?.length) {
+    return NextResponse.json(
+      { error: true, message: "Invalid request path" },
+      { status: 400 },
+    );
+  }
+
   const subpath = params.path.join("/");
-  if (!ALLOWED_PATH.has(subpath)) {
+  if (!VALID_PATHS.has(subpath)) {
     return NextResponse.json(
-      { error: true, msg: "you are not allowed to request " + subpath },
+      { error: true, message: "Invalid request path" },
       { status: 403 },
     );
   }

Committable suggestion skipped: line range outside the PR's diff.

}

const authResult = auth(req, ModelProvider.Bedrock);
if (authResult.error) {
return NextResponse.json(authResult, {
status: 401,
});
}

try {
return await requestBedrock(req);
} catch (e) {
console.error("Handler error:", e);
return NextResponse.json(prettyObject(e));
glayyiyi marked this conversation as resolved.
Show resolved Hide resolved
}
}
Comment on lines +152 to +177
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve handler security and validation

The handler implementation needs better security measures and input validation.

Apply these improvements:

 export async function handle(
   req: NextRequest,
   { params }: { params: { path: string[] } },
 ) {
+  // Validate path parameters
+  if (!params?.path?.length) {
+    return NextResponse.json(
+      { error: true, message: "Invalid request path" },
+      { status: 400 },
+    );
+  }
+
   const subpath = params.path.join("/");
   if (!ALLOWED_PATH.has(subpath)) {
     return NextResponse.json(
-      { error: true, msg: "you are not allowed to request " + subpath },
+      { error: true, message: "Invalid request path" },
       { status: 403 },
     );
   }

   const authResult = auth(req, ModelProvider.Bedrock);
   if (authResult.error) {
     return NextResponse.json(authResult, {
       status: 401,
     });
   }

   try {
     return await requestBedrock(req);
   } catch (e) {
-    console.error("Handler error:", e);
-    return NextResponse.json(prettyObject(e));
+    console.error("Request failed:", e instanceof Error ? e.message : "Unknown error");
+    return NextResponse.json(
+      { error: true, message: "Request processing failed" },
+      { status: 500 },
+    );
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export async function handle(
req: NextRequest,
{ params }: { params: { path: string[] } },
) {
const subpath = params.path.join("/");
if (!ALLOWED_PATH.has(subpath)) {
return NextResponse.json(
{ error: true, msg: "you are not allowed to request " + subpath },
{ status: 403 },
);
}
const authResult = auth(req, ModelProvider.Bedrock);
if (authResult.error) {
return NextResponse.json(authResult, {
status: 401,
});
}
try {
return await requestBedrock(req);
} catch (e) {
console.error("Handler error:", e);
return NextResponse.json(prettyObject(e));
}
}
export async function handle(
req: NextRequest,
{ params }: { params: { path: string[] } },
) {
// Validate path parameters
if (!params?.path?.length) {
return NextResponse.json(
{ error: true, message: "Invalid request path" },
{ status: 400 },
);
}
const subpath = params.path.join("/");
if (!ALLOWED_PATH.has(subpath)) {
return NextResponse.json(
{ error: true, message: "Invalid request path" },
{ status: 403 },
);
}
const authResult = auth(req, ModelProvider.Bedrock);
if (authResult.error) {
return NextResponse.json(authResult, {
status: 401,
});
}
try {
return await requestBedrock(req);
} catch (e) {
console.error("Request failed:", e instanceof Error ? e.message : "Unknown error");
return NextResponse.json(
{ error: true, message: "Request processing failed" },
{ status: 500 },
);
}
}

35 changes: 25 additions & 10 deletions app/client/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { MoonshotApi } from "./platforms/moonshot";
import { SparkApi } from "./platforms/iflytek";
import { XAIApi } from "./platforms/xai";
import { ChatGLMApi } from "./platforms/glm";
import { BedrockApi } from "./platforms/bedrock";

export const ROLES = ["system", "user", "assistant"] as const;
export type MessageRole = (typeof ROLES)[number];
Expand Down Expand Up @@ -130,6 +131,9 @@ export class ClientApi {

constructor(provider: ModelProvider = ModelProvider.GPT) {
switch (provider) {
case ModelProvider.Bedrock:
this.llm = new BedrockApi();
break;
case ModelProvider.GeminiPro:
this.llm = new GeminiProApi();
break;
Expand Down Expand Up @@ -239,6 +243,7 @@ export function getHeaders(ignoreHeaders: boolean = false) {

function getConfig() {
const modelConfig = chatStore.currentSession().mask.modelConfig;
const isBedrock = modelConfig.providerName === ServiceProvider.Bedrock;
const isGoogle = modelConfig.providerName === ServiceProvider.Google;
const isAzure = modelConfig.providerName === ServiceProvider.Azure;
const isAnthropic = modelConfig.providerName === ServiceProvider.Anthropic;
Expand Down Expand Up @@ -272,6 +277,7 @@ export function getHeaders(ignoreHeaders: boolean = false) {
: ""
: accessStore.openaiApiKey;
return {
isBedrock,
isGoogle,
isAzure,
isAnthropic,
Expand All @@ -298,6 +304,7 @@ export function getHeaders(ignoreHeaders: boolean = false) {
}

const {
isBedrock,
isGoogle,
isAzure,
isAnthropic,
Expand All @@ -310,24 +317,32 @@ export function getHeaders(ignoreHeaders: boolean = false) {

const authHeader = getAuthHeader();

const bearerToken = getBearerToken(
apiKey,
isAzure || isAnthropic || isGoogle,
);

if (bearerToken) {
headers[authHeader] = bearerToken;
} else if (isEnabledAccessControl && validString(accessStore.accessCode)) {
headers["Authorization"] = getBearerToken(
ACCESS_CODE_PREFIX + accessStore.accessCode,
if (isBedrock) {
if (apiKey) {
headers[authHeader] = getBearerToken(apiKey);
}
} else {
const bearerToken = getBearerToken(
apiKey,
isAzure || isAnthropic || isGoogle,
);

if (bearerToken) {
headers[authHeader] = bearerToken;
} else if (isEnabledAccessControl && validString(accessStore.accessCode)) {
headers["Authorization"] = getBearerToken(
ACCESS_CODE_PREFIX + accessStore.accessCode,
);
}
Comment on lines +335 to +351
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

⚠️ Potential issue

AWS Bedrock authentication implementation needs correction

The current implementation is incorrect as it uses a simple bearer token for Bedrock authentication when it should use AWS Signature Version 4. The codebase already has the proper AWS signing implementation in app/utils/aws.ts, but it's not being utilized in the API client.

  • app/client/api.ts: Replace the current Bedrock authentication with AWS Signature V4
  if (isBedrock) {
-    if (apiKey) {
-      headers[authHeader] = getBearerToken(apiKey);
-    }
+    if (!apiKey) {
+      throw new Error("AWS credentials are required for Bedrock");
+    }
+    // AWS credentials are already properly handled in BedrockApi class
+    // Remove this block entirely as authentication is managed by sign() function in aws.ts
  }

The review comment was correct about the authentication inconsistency, but the suggested fix was incomplete. The proper AWS Signature V4 implementation already exists in the codebase and is being used by the BedrockApi class through the sign() function in aws.ts. The authentication code in api.ts should be removed as it's redundant and incorrect.

🔗 Analysis chain

Critical: Inconsistent authentication handling for Bedrock

The current implementation has inconsistencies in how it handles authentication for Bedrock:

  1. Uses authHeader but doesn't utilize the AWS credentials from the config
  2. Doesn't follow AWS best practices for request signing

Consider implementing AWS Signature Version 4 signing:

  if (isBedrock) {
-    if (apiKey) {
-      headers[authHeader] = getBearerToken(apiKey);
-    }
+    if (!apiKey) {
+      throw new Error("AWS credentials are required for Bedrock");
+    }
+    const [region, accessKey, secretKey] = apiKey.split(":");
+    headers["X-Amz-Date"] = new Date().toISOString().replace(/[:-]|\.\d{3}/g, "");
+    headers["X-Amz-Region"] = region;
+    // Add AWS Signature V4 headers here
  } else {

Let's verify the AWS authentication implementation:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for AWS authentication implementation
ast-grep --pattern 'headers["X-Amz-$_"] = $_'

# Check for AWS SDK usage
rg -i "aws-sdk" package.json

Length of output: 76


Script:

#!/bin/bash
# Let's check for any AWS-related authentication or Bedrock implementation
rg -i "bedrock|aws" app/client/api.ts -A 5 -B 5

# Check for any AWS credential handling
rg -i "aws.*credentials|credentials.*aws" .

# Check for any existing authentication implementations
ast-grep --pattern 'function getBearerToken($_) {
  $$$
}'

Length of output: 4116


Script:

#!/bin/bash
# Let's check the Bedrock API implementation
rg -l "class BedrockApi" . | xargs cat

# Check AWS utility functions
cat app/utils/aws.ts

# Check getBearerToken implementation
rg -A 10 "function getBearerToken" app/client/api.ts

Length of output: 23479

}

return headers;
}

export function getClientApi(provider: ServiceProvider): ClientApi {
switch (provider) {
case ServiceProvider.Bedrock:
return new ClientApi(ModelProvider.Bedrock);
case ServiceProvider.Google:
return new ClientApi(ModelProvider.GeminiPro);
case ServiceProvider.Anthropic:
Expand Down
Loading