mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-10-04 00:56:40 +08:00
commit
377db17aa0
80
.github/ISSUE_TEMPLATE/1_bug_report.yml
vendored
Normal file
80
.github/ISSUE_TEMPLATE/1_bug_report.yml
vendored
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
name: '🐛 Bug Report'
|
||||||
|
description: 'Report an bug'
|
||||||
|
title: '[Bug] '
|
||||||
|
labels: ['bug']
|
||||||
|
body:
|
||||||
|
- type: dropdown
|
||||||
|
attributes:
|
||||||
|
label: '📦 Deployment Method'
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- 'Official installation package'
|
||||||
|
- 'Vercel'
|
||||||
|
- 'Zeabur'
|
||||||
|
- 'Sealos'
|
||||||
|
- 'Netlify'
|
||||||
|
- 'Docker'
|
||||||
|
- 'Other'
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: '📌 Version'
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
attributes:
|
||||||
|
label: '💻 Operating System'
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- 'Windows'
|
||||||
|
- 'macOS'
|
||||||
|
- 'Ubuntu'
|
||||||
|
- 'Other Linux'
|
||||||
|
- 'iOS'
|
||||||
|
- 'iPad OS'
|
||||||
|
- 'Android'
|
||||||
|
- 'Other'
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: '📌 System Version'
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
attributes:
|
||||||
|
label: '🌐 Browser'
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- 'Chrome'
|
||||||
|
- 'Edge'
|
||||||
|
- 'Safari'
|
||||||
|
- 'Firefox'
|
||||||
|
- 'Other'
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: '📌 Browser Version'
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: '🐛 Bug Description'
|
||||||
|
description: A clear and concise description of the bug, if the above option is `Other`, please also explain in detail.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: '📷 Recurrence Steps'
|
||||||
|
description: A clear and concise description of how to recurrence.
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: '🚦 Expected Behavior'
|
||||||
|
description: A clear and concise description of what you expected to happen.
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: '📝 Additional Information'
|
||||||
|
description: If your problem needs further explanation, or if the issue you're seeing cannot be reproduced in a gist, please add more information here.
|
80
.github/ISSUE_TEMPLATE/1_bug_report_cn.yml
vendored
Normal file
80
.github/ISSUE_TEMPLATE/1_bug_report_cn.yml
vendored
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
name: '🐛 反馈缺陷'
|
||||||
|
description: '反馈一个问题/缺陷'
|
||||||
|
title: '[Bug] '
|
||||||
|
labels: ['bug']
|
||||||
|
body:
|
||||||
|
- type: dropdown
|
||||||
|
attributes:
|
||||||
|
label: '📦 部署方式'
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- '官方安装包'
|
||||||
|
- 'Vercel'
|
||||||
|
- 'Zeabur'
|
||||||
|
- 'Sealos'
|
||||||
|
- 'Netlify'
|
||||||
|
- 'Docker'
|
||||||
|
- 'Other'
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: '📌 软件版本'
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
attributes:
|
||||||
|
label: '💻 系统环境'
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- 'Windows'
|
||||||
|
- 'macOS'
|
||||||
|
- 'Ubuntu'
|
||||||
|
- 'Other Linux'
|
||||||
|
- 'iOS'
|
||||||
|
- 'iPad OS'
|
||||||
|
- 'Android'
|
||||||
|
- 'Other'
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: '📌 系统版本'
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
attributes:
|
||||||
|
label: '🌐 浏览器'
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- 'Chrome'
|
||||||
|
- 'Edge'
|
||||||
|
- 'Safari'
|
||||||
|
- 'Firefox'
|
||||||
|
- 'Other'
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: '📌 浏览器版本'
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: '🐛 问题描述'
|
||||||
|
description: 请提供一个清晰且简洁的问题描述,若上述选项为`Other`,也请详细说明。
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: '📷 复现步骤'
|
||||||
|
description: 请提供一个清晰且简洁的描述,说明如何复现问题。
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: '🚦 期望结果'
|
||||||
|
description: 请提供一个清晰且简洁的描述,说明您期望发生什么。
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: '📝 补充信息'
|
||||||
|
description: 如果您的问题需要进一步说明,或者您遇到的问题无法在一个简单的示例中复现,请在这里添加更多信息。
|
21
.github/ISSUE_TEMPLATE/2_feature_request.yml
vendored
Normal file
21
.github/ISSUE_TEMPLATE/2_feature_request.yml
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
name: '🌠 Feature Request'
|
||||||
|
description: 'Suggest an idea'
|
||||||
|
title: '[Feature Request] '
|
||||||
|
labels: ['enhancement']
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: '🥰 Feature Description'
|
||||||
|
description: Please add a clear and concise description of the problem you are seeking to solve with this feature request.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: '🧐 Proposed Solution'
|
||||||
|
description: Describe the solution you'd like in a clear and concise manner.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: '📝 Additional Information'
|
||||||
|
description: Add any other context about the problem here.
|
21
.github/ISSUE_TEMPLATE/2_feature_request_cn.yml
vendored
Normal file
21
.github/ISSUE_TEMPLATE/2_feature_request_cn.yml
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
name: '🌠 功能需求'
|
||||||
|
description: '提出需求或建议'
|
||||||
|
title: '[Feature Request] '
|
||||||
|
labels: ['enhancement']
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: '🥰 需求描述'
|
||||||
|
description: 请添加一个清晰且简洁的问题描述,阐述您希望通过这个功能需求解决的问题。
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: '🧐 解决方案'
|
||||||
|
description: 请清晰且简洁地描述您想要的解决方案。
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: '📝 补充信息'
|
||||||
|
description: 在这里添加关于问题的任何其他背景信息。
|
146
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
146
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -1,146 +0,0 @@
|
|||||||
name: Bug report
|
|
||||||
description: Create a report to help us improve
|
|
||||||
title: "[Bug] "
|
|
||||||
labels: ["bug"]
|
|
||||||
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: "## Describe the bug"
|
|
||||||
- type: textarea
|
|
||||||
id: bug-description
|
|
||||||
attributes:
|
|
||||||
label: "Bug Description"
|
|
||||||
description: "A clear and concise description of what the bug is."
|
|
||||||
placeholder: "Explain the bug..."
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: "## To Reproduce"
|
|
||||||
- type: textarea
|
|
||||||
id: steps-to-reproduce
|
|
||||||
attributes:
|
|
||||||
label: "Steps to Reproduce"
|
|
||||||
description: "Steps to reproduce the behavior:"
|
|
||||||
placeholder: |
|
|
||||||
1. Go to '...'
|
|
||||||
2. Click on '....'
|
|
||||||
3. Scroll down to '....'
|
|
||||||
4. See error
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: "## Expected behavior"
|
|
||||||
- type: textarea
|
|
||||||
id: expected-behavior
|
|
||||||
attributes:
|
|
||||||
label: "Expected Behavior"
|
|
||||||
description: "A clear and concise description of what you expected to happen."
|
|
||||||
placeholder: "Describe what you expected to happen..."
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: "## Screenshots"
|
|
||||||
- type: textarea
|
|
||||||
id: screenshots
|
|
||||||
attributes:
|
|
||||||
label: "Screenshots"
|
|
||||||
description: "If applicable, add screenshots to help explain your problem."
|
|
||||||
placeholder: "Paste your screenshots here or write 'N/A' if not applicable..."
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: "## Deployment"
|
|
||||||
- type: checkboxes
|
|
||||||
id: deployment
|
|
||||||
attributes:
|
|
||||||
label: "Deployment Method"
|
|
||||||
description: "Please select the deployment method you are using."
|
|
||||||
options:
|
|
||||||
- label: "Docker"
|
|
||||||
- label: "Vercel"
|
|
||||||
- label: "Server"
|
|
||||||
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: "## Desktop (please complete the following information):"
|
|
||||||
- type: input
|
|
||||||
id: desktop-os
|
|
||||||
attributes:
|
|
||||||
label: "Desktop OS"
|
|
||||||
description: "Your desktop operating system."
|
|
||||||
placeholder: "e.g., Windows 10"
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: input
|
|
||||||
id: desktop-browser
|
|
||||||
attributes:
|
|
||||||
label: "Desktop Browser"
|
|
||||||
description: "Your desktop browser."
|
|
||||||
placeholder: "e.g., Chrome, Safari"
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: input
|
|
||||||
id: desktop-version
|
|
||||||
attributes:
|
|
||||||
label: "Desktop Browser Version"
|
|
||||||
description: "Version of your desktop browser."
|
|
||||||
placeholder: "e.g., 89.0"
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: "## Smartphone (please complete the following information):"
|
|
||||||
- type: input
|
|
||||||
id: smartphone-device
|
|
||||||
attributes:
|
|
||||||
label: "Smartphone Device"
|
|
||||||
description: "Your smartphone device."
|
|
||||||
placeholder: "e.g., iPhone X"
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: input
|
|
||||||
id: smartphone-os
|
|
||||||
attributes:
|
|
||||||
label: "Smartphone OS"
|
|
||||||
description: "Your smartphone operating system."
|
|
||||||
placeholder: "e.g., iOS 14.4"
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: input
|
|
||||||
id: smartphone-browser
|
|
||||||
attributes:
|
|
||||||
label: "Smartphone Browser"
|
|
||||||
description: "Your smartphone browser."
|
|
||||||
placeholder: "e.g., Safari"
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: input
|
|
||||||
id: smartphone-version
|
|
||||||
attributes:
|
|
||||||
label: "Smartphone Browser Version"
|
|
||||||
description: "Version of your smartphone browser."
|
|
||||||
placeholder: "e.g., 14"
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: "## Additional Logs"
|
|
||||||
- type: textarea
|
|
||||||
id: additional-logs
|
|
||||||
attributes:
|
|
||||||
label: "Additional Logs"
|
|
||||||
description: "Add any logs about the problem here."
|
|
||||||
placeholder: "Paste any relevant logs here..."
|
|
||||||
validations:
|
|
||||||
required: false
|
|
53
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
53
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@ -1,53 +0,0 @@
|
|||||||
name: Feature request
|
|
||||||
description: Suggest an idea for this project
|
|
||||||
title: "[Feature Request]: "
|
|
||||||
labels: ["enhancement"]
|
|
||||||
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: "## Is your feature request related to a problem? Please describe."
|
|
||||||
- type: textarea
|
|
||||||
id: problem-description
|
|
||||||
attributes:
|
|
||||||
label: Problem Description
|
|
||||||
description: "A clear and concise description of what the problem is. Example: I'm always frustrated when [...]"
|
|
||||||
placeholder: "Explain the problem you are facing..."
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: "## Describe the solution you'd like"
|
|
||||||
- type: textarea
|
|
||||||
id: desired-solution
|
|
||||||
attributes:
|
|
||||||
label: Solution Description
|
|
||||||
description: A clear and concise description of what you want to happen.
|
|
||||||
placeholder: "Describe the solution you'd like..."
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: "## Describe alternatives you've considered"
|
|
||||||
- type: textarea
|
|
||||||
id: alternatives-considered
|
|
||||||
attributes:
|
|
||||||
label: Alternatives Considered
|
|
||||||
description: A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
placeholder: "Describe any alternative solutions or features you've considered..."
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: "## Additional context"
|
|
||||||
- type: textarea
|
|
||||||
id: additional-context
|
|
||||||
attributes:
|
|
||||||
label: Additional Context
|
|
||||||
description: Add any other context or screenshots about the feature request here.
|
|
||||||
placeholder: "Add any other context or screenshots about the feature request here..."
|
|
||||||
validations:
|
|
||||||
required: false
|
|
28
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
28
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#### 💻 变更类型 | Change Type
|
||||||
|
|
||||||
|
<!-- For change type, change [ ] to [x]. -->
|
||||||
|
|
||||||
|
- [ ] feat <!-- 引入新功能 | Introduce new features -->
|
||||||
|
- [ ] fix <!-- 修复 Bug | Fix a bug -->
|
||||||
|
- [ ] refactor <!-- 重构代码(既不修复 Bug 也不添加新功能) | Refactor code that neither fixes a bug nor adds a feature -->
|
||||||
|
- [ ] perf <!-- 提升性能的代码变更 | A code change that improves performance -->
|
||||||
|
- [ ] style <!-- 添加或更新不影响代码含义的样式文件 | Add or update style files that do not affect the meaning of the code -->
|
||||||
|
- [ ] test <!-- 添加缺失的测试或纠正现有的测试 | Adding missing tests or correcting existing tests -->
|
||||||
|
- [ ] docs <!-- 仅文档更新 | Documentation only changes -->
|
||||||
|
- [ ] ci <!-- 修改持续集成配置文件和脚本 | Changes to our CI configuration files and scripts -->
|
||||||
|
- [ ] chore <!-- 其他不修改 src 或 test 文件的变更 | Other changes that don’t modify src or test files -->
|
||||||
|
- [ ] build <!-- 进行架构变更 | Make architectural changes -->
|
||||||
|
|
||||||
|
#### 🔀 变更说明 | Description of Change
|
||||||
|
|
||||||
|
<!--
|
||||||
|
感谢您的 Pull Request ,请提供此 Pull Request 的变更说明
|
||||||
|
Thank you for your Pull Request. Please provide a description above.
|
||||||
|
-->
|
||||||
|
|
||||||
|
#### 📝 补充信息 | Additional Information
|
||||||
|
|
||||||
|
<!--
|
||||||
|
请添加与此 Pull Request 相关的补充信息
|
||||||
|
Add any other context about the Pull Request here.
|
||||||
|
-->
|
@ -11,6 +11,7 @@ import { prettyObject } from "@/app/utils/format";
|
|||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import { auth } from "../../auth";
|
import { auth } from "../../auth";
|
||||||
import { isModelAvailableInServer } from "@/app/utils/model";
|
import { isModelAvailableInServer } from "@/app/utils/model";
|
||||||
|
import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare";
|
||||||
|
|
||||||
const ALLOWD_PATH = new Set([Anthropic.ChatPath, Anthropic.ChatPath1]);
|
const ALLOWD_PATH = new Set([Anthropic.ChatPath, Anthropic.ChatPath1]);
|
||||||
|
|
||||||
@ -114,7 +115,8 @@ async function request(req: NextRequest) {
|
|||||||
10 * 60 * 1000,
|
10 * 60 * 1000,
|
||||||
);
|
);
|
||||||
|
|
||||||
const fetchUrl = `${baseUrl}${path}`;
|
// try rebuild url, when using cloudflare ai gateway in server
|
||||||
|
const fetchUrl = cloudflareAIGatewayUrl(`${baseUrl}${path}`);
|
||||||
|
|
||||||
const fetchOptions: RequestInit = {
|
const fetchOptions: RequestInit = {
|
||||||
headers: {
|
headers: {
|
||||||
@ -164,17 +166,17 @@ async function request(req: NextRequest) {
|
|||||||
console.error(`[Anthropic] filter`, e);
|
console.error(`[Anthropic] filter`, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log("[Anthropic request]", fetchOptions.headers, req.method);
|
// console.log("[Anthropic request]", fetchOptions.headers, req.method);
|
||||||
try {
|
try {
|
||||||
const res = await fetch(fetchUrl, fetchOptions);
|
const res = await fetch(fetchUrl, fetchOptions);
|
||||||
|
|
||||||
console.log(
|
// console.log(
|
||||||
"[Anthropic response]",
|
// "[Anthropic response]",
|
||||||
res.status,
|
// res.status,
|
||||||
" ",
|
// " ",
|
||||||
res.headers,
|
// res.headers,
|
||||||
res.url,
|
// res.url,
|
||||||
);
|
// );
|
||||||
// to prevent browser prompt for credentials
|
// to prevent browser prompt for credentials
|
||||||
const newHeaders = new Headers(res.headers);
|
const newHeaders = new Headers(res.headers);
|
||||||
newHeaders.delete("www-authenticate");
|
newHeaders.delete("www-authenticate");
|
||||||
|
@ -14,6 +14,7 @@ import prisma from "@/lib/prisma";
|
|||||||
import { getTokenLength } from "@/lib/utils";
|
import { getTokenLength } from "@/lib/utils";
|
||||||
|
|
||||||
import { isModelAvailableInServer } from "../utils/model";
|
import { isModelAvailableInServer } from "../utils/model";
|
||||||
|
import { cloudflareAIGatewayUrl } from "../utils/cloudflare";
|
||||||
|
|
||||||
const serverConfig = getServerSideConfig();
|
const serverConfig = getServerSideConfig();
|
||||||
|
|
||||||
@ -109,8 +110,10 @@ export async function requestOpenai(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchUrl = `${baseUrl}/${path}`;
|
// const fetchUrl = `${baseUrl}/${path}`;
|
||||||
const jsonBody = await req.json();
|
const jsonBody = await req.json();
|
||||||
|
const fetchUrl = cloudflareAIGatewayUrl(`${baseUrl}/${path}`);
|
||||||
|
console.log("fetchUrl", fetchUrl);
|
||||||
const fetchOptions: RequestInit = {
|
const fetchOptions: RequestInit = {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
@ -12,6 +12,7 @@ import {
|
|||||||
import Locale from "../../locales";
|
import Locale from "../../locales";
|
||||||
import { prettyObject } from "@/app/utils/format";
|
import { prettyObject } from "@/app/utils/format";
|
||||||
import { getMessageTextContent, isVisionModel } from "@/app/utils";
|
import { getMessageTextContent, isVisionModel } from "@/app/utils";
|
||||||
|
import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare";
|
||||||
|
|
||||||
export type MultiBlockContent = {
|
export type MultiBlockContent = {
|
||||||
type: "image" | "text";
|
type: "image" | "text";
|
||||||
@ -375,7 +376,8 @@ export class ClaudeApi implements LLMApi {
|
|||||||
|
|
||||||
baseUrl = trimEnd(baseUrl, "/");
|
baseUrl = trimEnd(baseUrl, "/");
|
||||||
|
|
||||||
return `${baseUrl}/${path}`;
|
// try rebuild url, when using cloudflare ai gateway in client
|
||||||
|
return cloudflareAIGatewayUrl(`${baseUrl}/${path}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,16 +122,13 @@ export class GeminiProApi implements LLMApi {
|
|||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
options.onController?.(controller);
|
options.onController?.(controller);
|
||||||
try {
|
try {
|
||||||
// let baseUrl = accessStore.googleUrl;
|
if (!baseUrl && isApp) {
|
||||||
|
baseUrl = DEFAULT_API_HOST + "/api/proxy/google/";
|
||||||
if (!baseUrl) {
|
|
||||||
baseUrl = isApp
|
|
||||||
? DEFAULT_API_HOST +
|
|
||||||
"/api/proxy/google/" +
|
|
||||||
Google.ChatPath(modelConfig.model)
|
|
||||||
: this.path(Google.ChatPath(modelConfig.model));
|
|
||||||
}
|
}
|
||||||
|
baseUrl = `${baseUrl}/${Google.ChatPath(modelConfig.model)}`.replaceAll(
|
||||||
|
"//",
|
||||||
|
"/",
|
||||||
|
);
|
||||||
if (isApp) {
|
if (isApp) {
|
||||||
baseUrl += `?key=${accessStore.googleApiKey}`;
|
baseUrl += `?key=${accessStore.googleApiKey}`;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import {
|
|||||||
} from "@/app/constant";
|
} from "@/app/constant";
|
||||||
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
|
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
|
||||||
import { collectModelsWithDefaultModel } from "@/app/utils/model";
|
import { collectModelsWithDefaultModel } from "@/app/utils/model";
|
||||||
|
import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ChatOptions,
|
ChatOptions,
|
||||||
@ -95,7 +96,8 @@ export class ChatGPTApi implements LLMApi {
|
|||||||
|
|
||||||
console.log("[Proxy Endpoint] ", baseUrl, path);
|
console.log("[Proxy Endpoint] ", baseUrl, path);
|
||||||
|
|
||||||
return [baseUrl, path].join("/");
|
// try rebuild url, when using cloudflare ai gateway in client
|
||||||
|
return cloudflareAIGatewayUrl([baseUrl, path].join("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
extractMessage(res: any) {
|
extractMessage(res: any) {
|
||||||
|
@ -257,11 +257,11 @@ function useSubmitHandler() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RenderPompt = Pick<Prompt, "title" | "content">;
|
export type RenderPrompt = Pick<Prompt, "title" | "content">;
|
||||||
|
|
||||||
export function PromptHints(props: {
|
export function PromptHints(props: {
|
||||||
prompts: RenderPompt[];
|
prompts: RenderPrompt[];
|
||||||
onPromptSelect: (prompt: RenderPompt) => void;
|
onPromptSelect: (prompt: RenderPrompt) => void;
|
||||||
}) {
|
}) {
|
||||||
const noPrompts = props.prompts.length === 0;
|
const noPrompts = props.prompts.length === 0;
|
||||||
const [selectIndex, setSelectIndex] = useState(0);
|
const [selectIndex, setSelectIndex] = useState(0);
|
||||||
@ -784,7 +784,7 @@ function _Chat() {
|
|||||||
|
|
||||||
// prompt hints
|
// prompt hints
|
||||||
const promptStore = usePromptStore();
|
const promptStore = usePromptStore();
|
||||||
const [promptHints, setPromptHints] = useState<RenderPompt[]>([]);
|
const [promptHints, setPromptHints] = useState<RenderPrompt[]>([]);
|
||||||
const onSearch = useDebouncedCallback(
|
const onSearch = useDebouncedCallback(
|
||||||
(text: string) => {
|
(text: string) => {
|
||||||
const matchedPrompts = promptStore.search(text);
|
const matchedPrompts = promptStore.search(text);
|
||||||
@ -873,7 +873,7 @@ function _Chat() {
|
|||||||
setAutoScroll(true);
|
setAutoScroll(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPromptSelect = (prompt: RenderPompt) => {
|
const onPromptSelect = (prompt: RenderPrompt) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setPromptHints([]);
|
setPromptHints([]);
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ export enum ApiPath {
|
|||||||
Azure = "/api/azure",
|
Azure = "/api/azure",
|
||||||
OpenAI = "/api/openai",
|
OpenAI = "/api/openai",
|
||||||
Anthropic = "/api/anthropic",
|
Anthropic = "/api/anthropic",
|
||||||
|
Google = "/api/google",
|
||||||
Baidu = "/api/baidu",
|
Baidu = "/api/baidu",
|
||||||
ByteDance = "/api/bytedance",
|
ByteDance = "/api/bytedance",
|
||||||
Alibaba = "/api/alibaba",
|
Alibaba = "/api/alibaba",
|
||||||
|
@ -389,18 +389,18 @@ const cn = {
|
|||||||
},
|
},
|
||||||
Baidu: {
|
Baidu: {
|
||||||
ApiKey: {
|
ApiKey: {
|
||||||
Title: "接口密钥",
|
Title: "API Key",
|
||||||
SubTitle: "使用自定义 Baidu API Key",
|
SubTitle: "使用自定义 Baidu API Key",
|
||||||
Placeholder: "Baidu API Key",
|
Placeholder: "Baidu API Key",
|
||||||
},
|
},
|
||||||
SecretKey: {
|
SecretKey: {
|
||||||
Title: "接口密钥",
|
Title: "Secret Key",
|
||||||
SubTitle: "使用自定义 Baidu Secret Key",
|
SubTitle: "使用自定义 Baidu Secret Key",
|
||||||
Placeholder: "Baidu Secret Key",
|
Placeholder: "Baidu Secret Key",
|
||||||
},
|
},
|
||||||
Endpoint: {
|
Endpoint: {
|
||||||
Title: "接口地址",
|
Title: "接口地址",
|
||||||
SubTitle: "样例:",
|
SubTitle: "不支持自定义前往.env配置",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ByteDance: {
|
ByteDance: {
|
||||||
|
@ -366,7 +366,7 @@ const en: LocaleType = {
|
|||||||
|
|
||||||
Endpoint: {
|
Endpoint: {
|
||||||
Title: "Endpoint Address",
|
Title: "Endpoint Address",
|
||||||
SubTitle: "Example:",
|
SubTitle: "Example: ",
|
||||||
},
|
},
|
||||||
|
|
||||||
ApiVerion: {
|
ApiVerion: {
|
||||||
@ -387,7 +387,7 @@ const en: LocaleType = {
|
|||||||
},
|
},
|
||||||
Endpoint: {
|
Endpoint: {
|
||||||
Title: "Endpoint Address",
|
Title: "Endpoint Address",
|
||||||
SubTitle: "Example:",
|
SubTitle: "not supported, configure in .env",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ByteDance: {
|
ByteDance: {
|
||||||
@ -398,7 +398,7 @@ const en: LocaleType = {
|
|||||||
},
|
},
|
||||||
Endpoint: {
|
Endpoint: {
|
||||||
Title: "Endpoint Address",
|
Title: "Endpoint Address",
|
||||||
SubTitle: "Example:",
|
SubTitle: "Example: ",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Alibaba: {
|
Alibaba: {
|
||||||
@ -409,7 +409,7 @@ const en: LocaleType = {
|
|||||||
},
|
},
|
||||||
Endpoint: {
|
Endpoint: {
|
||||||
Title: "Endpoint Address",
|
Title: "Endpoint Address",
|
||||||
SubTitle: "Example:",
|
SubTitle: "Example: ",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
CustomModel: {
|
CustomModel: {
|
||||||
@ -425,7 +425,7 @@ const en: LocaleType = {
|
|||||||
|
|
||||||
Endpoint: {
|
Endpoint: {
|
||||||
Title: "Endpoint Address",
|
Title: "Endpoint Address",
|
||||||
SubTitle: "Example:",
|
SubTitle: "Example: ",
|
||||||
},
|
},
|
||||||
|
|
||||||
ApiVersion: {
|
ApiVersion: {
|
||||||
|
@ -12,15 +12,33 @@ import { DEFAULT_CONFIG } from "./config";
|
|||||||
|
|
||||||
let fetchState = 0; // 0 not fetch, 1 fetching, 2 done
|
let fetchState = 0; // 0 not fetch, 1 fetching, 2 done
|
||||||
|
|
||||||
const DEFAULT_OPENAI_URL =
|
const isApp = getClientConfig()?.buildMode === "export";
|
||||||
getClientConfig()?.buildMode === "export"
|
|
||||||
? DEFAULT_API_HOST + "/api/proxy/openai"
|
|
||||||
: ApiPath.OpenAI;
|
|
||||||
|
|
||||||
const DEFAULT_AZURE_URL =
|
const DEFAULT_OPENAI_URL = isApp
|
||||||
getClientConfig()?.buildMode === "export"
|
? DEFAULT_API_HOST + "/api/proxy/openai"
|
||||||
? DEFAULT_API_HOST + "/api/proxy/azure/{resource_name}"
|
: ApiPath.OpenAI;
|
||||||
: ApiPath.Azure;
|
|
||||||
|
const DEFAULT_GOOGLE_URL = isApp
|
||||||
|
? DEFAULT_API_HOST + "/api/proxy/google"
|
||||||
|
: ApiPath.Google;
|
||||||
|
|
||||||
|
const DEFAULT_ANTHROPIC_URL = isApp
|
||||||
|
? DEFAULT_API_HOST + "/api/proxy/anthropic"
|
||||||
|
: ApiPath.Anthropic;
|
||||||
|
|
||||||
|
const DEFAULT_BAIDU_URL = isApp
|
||||||
|
? DEFAULT_API_HOST + "/api/proxy/baidu"
|
||||||
|
: ApiPath.Baidu;
|
||||||
|
|
||||||
|
const DEFAULT_BYTEDANCE_URL = isApp
|
||||||
|
? DEFAULT_API_HOST + "/api/proxy/bytedance"
|
||||||
|
: ApiPath.ByteDance;
|
||||||
|
|
||||||
|
const DEFAULT_ALIBABA_URL = isApp
|
||||||
|
? DEFAULT_API_HOST + "/api/proxy/alibaba"
|
||||||
|
: ApiPath.Alibaba;
|
||||||
|
|
||||||
|
// console.log("DEFAULT_ANTHROPIC_URL", DEFAULT_ANTHROPIC_URL);
|
||||||
|
|
||||||
const DEFAULT_ACCESS_STATE = {
|
const DEFAULT_ACCESS_STATE = {
|
||||||
accessCode: "",
|
accessCode: "",
|
||||||
@ -33,32 +51,32 @@ const DEFAULT_ACCESS_STATE = {
|
|||||||
openaiApiKey: "",
|
openaiApiKey: "",
|
||||||
|
|
||||||
// azure
|
// azure
|
||||||
azureUrl: DEFAULT_AZURE_URL,
|
azureUrl: "",
|
||||||
azureApiKey: "",
|
azureApiKey: "",
|
||||||
azureApiVersion: "2023-05-15",
|
azureApiVersion: "2023-05-15",
|
||||||
azureVoiceKey: "",
|
azureVoiceKey: "",
|
||||||
|
|
||||||
// google ai studio
|
// google ai studio
|
||||||
googleUrl: "",
|
googleUrl: DEFAULT_GOOGLE_URL,
|
||||||
googleApiKey: "",
|
googleApiKey: "",
|
||||||
googleApiVersion: "v1",
|
googleApiVersion: "v1",
|
||||||
|
|
||||||
// anthropic
|
// anthropic
|
||||||
|
anthropicUrl: DEFAULT_ANTHROPIC_URL,
|
||||||
anthropicApiKey: "",
|
anthropicApiKey: "",
|
||||||
anthropicApiVersion: "2023-06-01",
|
anthropicApiVersion: "2023-06-01",
|
||||||
anthropicUrl: "",
|
|
||||||
|
|
||||||
// baidu
|
// baidu
|
||||||
baiduUrl: "",
|
baiduUrl: DEFAULT_BAIDU_URL,
|
||||||
baiduApiKey: "",
|
baiduApiKey: "",
|
||||||
baiduSecretKey: "",
|
baiduSecretKey: "",
|
||||||
|
|
||||||
// bytedance
|
// bytedance
|
||||||
|
bytedanceUrl: DEFAULT_BYTEDANCE_URL,
|
||||||
bytedanceApiKey: "",
|
bytedanceApiKey: "",
|
||||||
bytedanceUrl: "",
|
|
||||||
|
|
||||||
// alibaba
|
// alibaba
|
||||||
alibabaUrl: "",
|
alibabaUrl: DEFAULT_ALIBABA_URL,
|
||||||
alibabaApiKey: "",
|
alibabaApiKey: "",
|
||||||
|
|
||||||
// server config
|
// server config
|
||||||
|
26
app/utils/cloudflare.ts
Normal file
26
app/utils/cloudflare.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
export function cloudflareAIGatewayUrl(fetchUrl: string) {
|
||||||
|
// rebuild fetchUrl, if using cloudflare ai gateway
|
||||||
|
// document: https://developers.cloudflare.com/ai-gateway/providers/openai/
|
||||||
|
|
||||||
|
const paths = fetchUrl.split("/");
|
||||||
|
if ("gateway.ai.cloudflare.com" == paths[2]) {
|
||||||
|
// is cloudflare.com ai gateway
|
||||||
|
// https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_id}/azure-openai/{resource_name}/{deployment_name}/chat/completions?api-version=2023-05-15'
|
||||||
|
if ("azure-openai" == paths[6]) {
|
||||||
|
// is azure gateway
|
||||||
|
return paths.slice(0, 8).concat(paths.slice(-3)).join("/"); // rebuild ai gateway azure_url
|
||||||
|
}
|
||||||
|
// https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_id}/openai/chat/completions
|
||||||
|
if ("openai" == paths[6]) {
|
||||||
|
// is openai gateway
|
||||||
|
return paths.slice(0, 7).concat(paths.slice(-2)).join("/"); // rebuild ai gateway openai_url
|
||||||
|
}
|
||||||
|
// https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_id}/anthropic/v1/messages \
|
||||||
|
if ("anthropic" == paths[6]) {
|
||||||
|
// is anthropic gateway
|
||||||
|
return paths.slice(0, 7).concat(paths.slice(-2)).join("/"); // rebuild ai gateway anthropic_url
|
||||||
|
}
|
||||||
|
// TODO: Amazon Bedrock, Groq, HuggingFace...
|
||||||
|
}
|
||||||
|
return fetchUrl;
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
import { DEFAULT_MODELS } from "../constant";
|
import { DEFAULT_MODELS } from "../constant";
|
||||||
import { LLMModel } from "../client/api";
|
import { LLMModel } from "../client/api";
|
||||||
|
|
||||||
const customProvider = (modelName: string) => ({
|
const customProvider = (providerName: string) => ({
|
||||||
id: modelName,
|
id: providerName.toLowerCase(),
|
||||||
providerName: "Custom",
|
providerName: providerName,
|
||||||
providerType: "custom",
|
providerType: "custom",
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -72,10 +72,17 @@ export function collectModelTable(
|
|||||||
}
|
}
|
||||||
// 2. if model not exists, create new model with available value
|
// 2. if model not exists, create new model with available value
|
||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
const provider = customProvider(name);
|
let [customModelName, customProviderName] = name.split("@");
|
||||||
modelTable[`${name}@${provider?.id}`] = {
|
const provider = customProvider(
|
||||||
name,
|
customProviderName || customModelName,
|
||||||
displayName: displayName || name,
|
);
|
||||||
|
// swap name and displayName for bytedance
|
||||||
|
if (displayName && provider.providerName == "ByteDance") {
|
||||||
|
[customModelName, displayName] = [displayName, customModelName];
|
||||||
|
}
|
||||||
|
modelTable[`${customModelName}@${provider?.id}`] = {
|
||||||
|
name: customModelName,
|
||||||
|
displayName: displayName || customModelName,
|
||||||
available,
|
available,
|
||||||
describe: "",
|
describe: "",
|
||||||
provider, // Use optional chaining
|
provider, // Use optional chaining
|
||||||
|
Binary file not shown.
@ -9,7 +9,7 @@
|
|||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "NextChat",
|
"productName": "NextChat",
|
||||||
"version": "2.12.4"
|
"version": "2.13.0"
|
||||||
},
|
},
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"allowlist": {
|
"allowlist": {
|
||||||
@ -112,4 +112,4 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user