Merge pull request #123 from sijinhui/dev

Dev
This commit is contained in:
sijinhui 2024-07-17 12:55:32 +08:00 committed by GitHub
commit 377db17aa0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 345 additions and 256 deletions

80
.github/ISSUE_TEMPLATE/1_bug_report.yml vendored Normal file
View 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.

View 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: 如果您的问题需要进一步说明,或者您遇到的问题无法在一个简单的示例中复现,请在这里添加更多信息。

View 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.

View 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: 在这里添加关于问题的任何其他背景信息。

View File

@ -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

View File

@ -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
View 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 dont 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.
-->

View File

@ -11,6 +11,7 @@ import { prettyObject } from "@/app/utils/format";
import { NextRequest, NextResponse } from "next/server";
import { auth } from "../../auth";
import { isModelAvailableInServer } from "@/app/utils/model";
import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare";
const ALLOWD_PATH = new Set([Anthropic.ChatPath, Anthropic.ChatPath1]);
@ -114,7 +115,8 @@ async function request(req: NextRequest) {
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 = {
headers: {
@ -164,17 +166,17 @@ async function request(req: NextRequest) {
console.error(`[Anthropic] filter`, e);
}
}
console.log("[Anthropic request]", fetchOptions.headers, req.method);
// console.log("[Anthropic request]", fetchOptions.headers, req.method);
try {
const res = await fetch(fetchUrl, fetchOptions);
console.log(
"[Anthropic response]",
res.status,
" ",
res.headers,
res.url,
);
// console.log(
// "[Anthropic response]",
// res.status,
// " ",
// res.headers,
// res.url,
// );
// to prevent browser prompt for credentials
const newHeaders = new Headers(res.headers);
newHeaders.delete("www-authenticate");

View File

@ -14,6 +14,7 @@ import prisma from "@/lib/prisma";
import { getTokenLength } from "@/lib/utils";
import { isModelAvailableInServer } from "../utils/model";
import { cloudflareAIGatewayUrl } from "../utils/cloudflare";
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 fetchUrl = cloudflareAIGatewayUrl(`${baseUrl}/${path}`);
console.log("fetchUrl", fetchUrl);
const fetchOptions: RequestInit = {
headers: {
"Content-Type": "application/json",

View File

@ -12,6 +12,7 @@ import {
import Locale from "../../locales";
import { prettyObject } from "@/app/utils/format";
import { getMessageTextContent, isVisionModel } from "@/app/utils";
import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare";
export type MultiBlockContent = {
type: "image" | "text";
@ -375,7 +376,8 @@ export class ClaudeApi implements LLMApi {
baseUrl = trimEnd(baseUrl, "/");
return `${baseUrl}/${path}`;
// try rebuild url, when using cloudflare ai gateway in client
return cloudflareAIGatewayUrl(`${baseUrl}/${path}`);
}
}

View File

@ -122,16 +122,13 @@ export class GeminiProApi implements LLMApi {
const controller = new AbortController();
options.onController?.(controller);
try {
// let baseUrl = accessStore.googleUrl;
if (!baseUrl) {
baseUrl = isApp
? DEFAULT_API_HOST +
"/api/proxy/google/" +
Google.ChatPath(modelConfig.model)
: this.path(Google.ChatPath(modelConfig.model));
if (!baseUrl && isApp) {
baseUrl = DEFAULT_API_HOST + "/api/proxy/google/";
}
baseUrl = `${baseUrl}/${Google.ChatPath(modelConfig.model)}`.replaceAll(
"//",
"/",
);
if (isApp) {
baseUrl += `?key=${accessStore.googleApiKey}`;
}

View File

@ -12,6 +12,7 @@ import {
} from "@/app/constant";
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
import { collectModelsWithDefaultModel } from "@/app/utils/model";
import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare";
import {
ChatOptions,
@ -95,7 +96,8 @@ export class ChatGPTApi implements LLMApi {
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) {

View File

@ -257,11 +257,11 @@ function useSubmitHandler() {
};
}
export type RenderPompt = Pick<Prompt, "title" | "content">;
export type RenderPrompt = Pick<Prompt, "title" | "content">;
export function PromptHints(props: {
prompts: RenderPompt[];
onPromptSelect: (prompt: RenderPompt) => void;
prompts: RenderPrompt[];
onPromptSelect: (prompt: RenderPrompt) => void;
}) {
const noPrompts = props.prompts.length === 0;
const [selectIndex, setSelectIndex] = useState(0);
@ -784,7 +784,7 @@ function _Chat() {
// prompt hints
const promptStore = usePromptStore();
const [promptHints, setPromptHints] = useState<RenderPompt[]>([]);
const [promptHints, setPromptHints] = useState<RenderPrompt[]>([]);
const onSearch = useDebouncedCallback(
(text: string) => {
const matchedPrompts = promptStore.search(text);
@ -873,7 +873,7 @@ function _Chat() {
setAutoScroll(true);
};
const onPromptSelect = (prompt: RenderPompt) => {
const onPromptSelect = (prompt: RenderPrompt) => {
setTimeout(() => {
setPromptHints([]);

View File

@ -36,6 +36,7 @@ export enum ApiPath {
Azure = "/api/azure",
OpenAI = "/api/openai",
Anthropic = "/api/anthropic",
Google = "/api/google",
Baidu = "/api/baidu",
ByteDance = "/api/bytedance",
Alibaba = "/api/alibaba",

View File

@ -389,18 +389,18 @@ const cn = {
},
Baidu: {
ApiKey: {
Title: "接口密钥",
Title: "API Key",
SubTitle: "使用自定义 Baidu API Key",
Placeholder: "Baidu API Key",
},
SecretKey: {
Title: "接口密钥",
Title: "Secret Key",
SubTitle: "使用自定义 Baidu Secret Key",
Placeholder: "Baidu Secret Key",
},
Endpoint: {
Title: "接口地址",
SubTitle: "样例:",
SubTitle: "不支持自定义前往.env配置",
},
},
ByteDance: {

View File

@ -366,7 +366,7 @@ const en: LocaleType = {
Endpoint: {
Title: "Endpoint Address",
SubTitle: "Example:",
SubTitle: "Example: ",
},
ApiVerion: {
@ -387,7 +387,7 @@ const en: LocaleType = {
},
Endpoint: {
Title: "Endpoint Address",
SubTitle: "Example:",
SubTitle: "not supported, configure in .env",
},
},
ByteDance: {
@ -398,7 +398,7 @@ const en: LocaleType = {
},
Endpoint: {
Title: "Endpoint Address",
SubTitle: "Example:",
SubTitle: "Example: ",
},
},
Alibaba: {
@ -409,7 +409,7 @@ const en: LocaleType = {
},
Endpoint: {
Title: "Endpoint Address",
SubTitle: "Example:",
SubTitle: "Example: ",
},
},
CustomModel: {
@ -425,7 +425,7 @@ const en: LocaleType = {
Endpoint: {
Title: "Endpoint Address",
SubTitle: "Example:",
SubTitle: "Example: ",
},
ApiVersion: {

View File

@ -12,15 +12,33 @@ import { DEFAULT_CONFIG } from "./config";
let fetchState = 0; // 0 not fetch, 1 fetching, 2 done
const DEFAULT_OPENAI_URL =
getClientConfig()?.buildMode === "export"
? DEFAULT_API_HOST + "/api/proxy/openai"
: ApiPath.OpenAI;
const isApp = getClientConfig()?.buildMode === "export";
const DEFAULT_AZURE_URL =
getClientConfig()?.buildMode === "export"
? DEFAULT_API_HOST + "/api/proxy/azure/{resource_name}"
: ApiPath.Azure;
const DEFAULT_OPENAI_URL = isApp
? DEFAULT_API_HOST + "/api/proxy/openai"
: ApiPath.OpenAI;
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 = {
accessCode: "",
@ -33,32 +51,32 @@ const DEFAULT_ACCESS_STATE = {
openaiApiKey: "",
// azure
azureUrl: DEFAULT_AZURE_URL,
azureUrl: "",
azureApiKey: "",
azureApiVersion: "2023-05-15",
azureVoiceKey: "",
// google ai studio
googleUrl: "",
googleUrl: DEFAULT_GOOGLE_URL,
googleApiKey: "",
googleApiVersion: "v1",
// anthropic
anthropicUrl: DEFAULT_ANTHROPIC_URL,
anthropicApiKey: "",
anthropicApiVersion: "2023-06-01",
anthropicUrl: "",
// baidu
baiduUrl: "",
baiduUrl: DEFAULT_BAIDU_URL,
baiduApiKey: "",
baiduSecretKey: "",
// bytedance
bytedanceUrl: DEFAULT_BYTEDANCE_URL,
bytedanceApiKey: "",
bytedanceUrl: "",
// alibaba
alibabaUrl: "",
alibabaUrl: DEFAULT_ALIBABA_URL,
alibabaApiKey: "",
// server config

26
app/utils/cloudflare.ts Normal file
View 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;
}

View File

@ -1,9 +1,9 @@
import { DEFAULT_MODELS } from "../constant";
import { LLMModel } from "../client/api";
const customProvider = (modelName: string) => ({
id: modelName,
providerName: "Custom",
const customProvider = (providerName: string) => ({
id: providerName.toLowerCase(),
providerName: providerName,
providerType: "custom",
});
@ -72,10 +72,17 @@ export function collectModelTable(
}
// 2. if model not exists, create new model with available value
if (count === 0) {
const provider = customProvider(name);
modelTable[`${name}@${provider?.id}`] = {
name,
displayName: displayName || name,
let [customModelName, customProviderName] = name.split("@");
const provider = customProvider(
customProviderName || customModelName,
);
// 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,
describe: "",
provider, // Use optional chaining

Binary file not shown.

View File

@ -9,7 +9,7 @@
},
"package": {
"productName": "NextChat",
"version": "2.12.4"
"version": "2.13.0"
},
"tauri": {
"allowlist": {
@ -112,4 +112,4 @@
}
]
}
}
}