mirror of
https://github.com/sqlchat/sqlchat.git
synced 2025-09-27 01:56:16 +08:00
feat: add openai api key and endpoint in setting (#23)
This commit is contained in:
@ -1,7 +1,14 @@
|
|||||||
import { head, last } from "lodash-es";
|
import { head, last } from "lodash-es";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
import { getAssistantById, getPromptGeneratorOfAssistant, useConversationStore, useMessageStore, useConnectionStore } from "@/store";
|
import {
|
||||||
|
getAssistantById,
|
||||||
|
getPromptGeneratorOfAssistant,
|
||||||
|
useConversationStore,
|
||||||
|
useMessageStore,
|
||||||
|
useConnectionStore,
|
||||||
|
useSettingStore,
|
||||||
|
} from "@/store";
|
||||||
import { CreatorRole, Message } from "@/types";
|
import { CreatorRole, Message } from "@/types";
|
||||||
import { countTextTokens, generateUUID } from "@/utils";
|
import { countTextTokens, generateUUID } from "@/utils";
|
||||||
import Header from "./Header";
|
import Header from "./Header";
|
||||||
@ -15,6 +22,7 @@ import DataStorageBanner from "../DataStorageBanner";
|
|||||||
const MAX_TOKENS = 4000;
|
const MAX_TOKENS = 4000;
|
||||||
|
|
||||||
const ConversationView = () => {
|
const ConversationView = () => {
|
||||||
|
const settingStore = useSettingStore();
|
||||||
const connectionStore = useConnectionStore();
|
const connectionStore = useConnectionStore();
|
||||||
const conversationStore = useConversationStore();
|
const conversationStore = useConversationStore();
|
||||||
const messageStore = useMessageStore();
|
const messageStore = useMessageStore();
|
||||||
@ -151,6 +159,7 @@ const ConversationView = () => {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
messages: formatedMessageList,
|
messages: formatedMessageList,
|
||||||
|
openAIApiConfig: settingStore.setting.openAIApiConfig,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ const LocaleSelector = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<select className="select select-bordered !h-auto !min-h-fit" value={locale} onChange={handleLocaleChange}>
|
<select className="select select-bordered select-sm" value={locale} onChange={handleLocaleChange}>
|
||||||
<option value="en">English</option>
|
<option value="en">English</option>
|
||||||
<option value="zh">简体中文</option>
|
<option value="zh">简体中文</option>
|
||||||
</select>
|
</select>
|
||||||
|
64
src/components/OpenAIApiConfigView.tsx
Normal file
64
src/components/OpenAIApiConfigView.tsx
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { useDebounce } from "react-use";
|
||||||
|
import { useSettingStore } from "@/store";
|
||||||
|
import { OpenAIApiConfig } from "@/types";
|
||||||
|
|
||||||
|
const OpenAIApiConfigView = () => {
|
||||||
|
const settingStore = useSettingStore();
|
||||||
|
const [openAIApiConfig, setOpenAIApiConfig] = useState(settingStore.setting.openAIApiConfig);
|
||||||
|
|
||||||
|
useDebounce(
|
||||||
|
() => {
|
||||||
|
settingStore.setOpenAIApiConfig(openAIApiConfig);
|
||||||
|
},
|
||||||
|
300,
|
||||||
|
[openAIApiConfig]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSetOpenAIApiConfig = (config: Partial<OpenAIApiConfig>) => {
|
||||||
|
setOpenAIApiConfig({
|
||||||
|
...openAIApiConfig,
|
||||||
|
...config,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h3>OpenAI API configuration</h3>
|
||||||
|
<div className="w-full border border-gray-200 p-4 rounded-lg">
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<label htmlFor="openai-api-key">Key</label>
|
||||||
|
<input
|
||||||
|
id="openai-api-key"
|
||||||
|
className="input input-bordered input-sm mt-1"
|
||||||
|
type="text"
|
||||||
|
placeholder="OpenAI API Key"
|
||||||
|
value={openAIApiConfig.key}
|
||||||
|
onChange={(e) => {
|
||||||
|
handleSetOpenAIApiConfig({
|
||||||
|
key: e.target.value,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col mt-2">
|
||||||
|
<label htmlFor="openai-api-endpoint">Endpoint</label>
|
||||||
|
<input
|
||||||
|
id="openai-api-endpoint"
|
||||||
|
className="input input-bordered input-sm mt-1"
|
||||||
|
type="text"
|
||||||
|
placeholder="OpenAI API Endpoint"
|
||||||
|
value={openAIApiConfig.endpoint}
|
||||||
|
onChange={(e) => {
|
||||||
|
handleSetOpenAIApiConfig({
|
||||||
|
endpoint: e.target.value,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default OpenAIApiConfigView;
|
@ -3,6 +3,7 @@ import Icon from "./Icon";
|
|||||||
import WeChatQRCodeView from "./WeChatQRCodeView";
|
import WeChatQRCodeView from "./WeChatQRCodeView";
|
||||||
import ClearDataButton from "./ClearDataButton";
|
import ClearDataButton from "./ClearDataButton";
|
||||||
import LocaleSelector from "./LocaleSelector";
|
import LocaleSelector from "./LocaleSelector";
|
||||||
|
import OpenAIApiConfigView from "./OpenAIApiConfigView";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
show: boolean;
|
show: boolean;
|
||||||
@ -40,6 +41,8 @@ const SettingModal = (props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<OpenAIApiConfigView />
|
||||||
|
|
||||||
<h3>Danger Zone</h3>
|
<h3>Danger Zone</h3>
|
||||||
<div className="w-full border border-red-200 p-4 rounded-lg">
|
<div className="w-full border border-red-200 p-4 rounded-lg">
|
||||||
<div className="w-full flex flex-row justify-between items-center gap-2">
|
<div className="w-full flex flex-row justify-between items-center gap-2">
|
||||||
|
@ -6,14 +6,19 @@ export const config = {
|
|||||||
runtime: "edge",
|
runtime: "edge",
|
||||||
};
|
};
|
||||||
|
|
||||||
const apiEndpoint = new URL(`${openAIApiEndpoint}/v1/chat/completions`);
|
const getApiEndpoint = (apiEndpoint: string) => {
|
||||||
|
return new URL(`${apiEndpoint}/v1/chat/completions`);
|
||||||
|
};
|
||||||
|
|
||||||
const handler = async (req: NextRequest) => {
|
const handler = async (req: NextRequest) => {
|
||||||
const reqBody = await req.json();
|
const reqBody = await req.json();
|
||||||
|
const openAIApiConfig = reqBody.openAIApiConfig;
|
||||||
|
const apiKey = openAIApiConfig?.key || openAIApiKey;
|
||||||
|
const apiEndpoint = getApiEndpoint(openAIApiConfig?.endpoint || openAIApiEndpoint);
|
||||||
const res = await fetch(apiEndpoint, {
|
const res = await fetch(apiEndpoint, {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: `Bearer ${openAIApiKey}`,
|
Authorization: `Bearer ${apiKey}`,
|
||||||
},
|
},
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { merge } from "lodash-es";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { persist } from "zustand/middleware";
|
import { persist } from "zustand/middleware";
|
||||||
import { Setting } from "@/types";
|
import { Setting } from "@/types";
|
||||||
@ -5,12 +6,17 @@ import { Setting } from "@/types";
|
|||||||
const getDefaultSetting = (): Setting => {
|
const getDefaultSetting = (): Setting => {
|
||||||
return {
|
return {
|
||||||
locale: "en",
|
locale: "en",
|
||||||
|
openAIApiConfig: {
|
||||||
|
key: "",
|
||||||
|
endpoint: "",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
interface SettingState {
|
interface SettingState {
|
||||||
setting: Setting;
|
setting: Setting;
|
||||||
setLocale: (locale: Setting["locale"]) => void;
|
setLocale: (locale: Setting["locale"]) => void;
|
||||||
|
setOpenAIApiConfig: (openAIApiConfig: Setting["openAIApiConfig"]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useSettingStore = create<SettingState>()(
|
export const useSettingStore = create<SettingState>()(
|
||||||
@ -26,9 +32,18 @@ export const useSettingStore = create<SettingState>()(
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
setOpenAIApiConfig: (openAIApiConfig: Setting["openAIApiConfig"]) => {
|
||||||
|
set({
|
||||||
|
setting: {
|
||||||
|
...get().setting,
|
||||||
|
openAIApiConfig,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: "setting-storage",
|
name: "setting-storage",
|
||||||
|
merge: (persistedState, currentState) => merge(currentState, persistedState),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
export type Locale = "en" | "zh";
|
export type Locale = "en" | "zh";
|
||||||
|
|
||||||
|
export interface OpenAIApiConfig {
|
||||||
|
key: string;
|
||||||
|
endpoint: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Setting {
|
export interface Setting {
|
||||||
locale: Locale;
|
locale: Locale;
|
||||||
|
openAIApiConfig: OpenAIApiConfig;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user