NextChat-U/app/client/api.ts

335 lines
8.7 KiB
TypeScript
Raw Normal View History

2023-06-16 00:55:18 +09:00
import { getClientConfig } from "../config/client";
2023-12-24 03:15:30 +09:00
import {
ACCESS_CODE_PREFIX,
ModelProvider,
ServiceProvider,
} from "../constant";
2024-08-29 00:58:46 +09:00
import {
ChatMessageTool,
ChatMessage,
ModelType,
useAccessStore,
useChatStore,
} from "../store";
2024-08-02 19:50:48 +09:00
import { ChatGPTApi, DalleRequestPayload } from "./platforms/openai";
2023-12-24 05:22:12 +09:00
import { GeminiProApi } from "./platforms/google";
2024-04-07 12:32:57 +09:00
import { ClaudeApi } from "./platforms/anthropic";
2024-07-06 14:05:09 +09:00
import { ErnieApi } from "./platforms/baidu";
2024-07-06 15:59:37 +09:00
import { DoubaoApi } from "./platforms/bytedance";
2024-07-07 22:59:56 +09:00
import { QwenApi } from "./platforms/alibaba";
2024-07-07 15:43:28 +09:00
import { HunyuanApi } from "./platforms/tencent";
2024-08-01 12:55:22 +09:00
import { MoonshotApi } from "./platforms/moonshot";
import { SparkApi } from "./platforms/iflytek";
2024-07-07 22:59:56 +09:00
export const ROLES = ["system", "user", "assistant"] as const;
export type MessageRole = (typeof ROLES)[number];
2023-05-15 00:00:17 +09:00
export const Models = ["gpt-3.5-turbo", "gpt-4"] as const;
2024-08-27 17:21:02 +09:00
export const TTSModels = ["tts-1", "tts-1-hd"] as const;
2023-05-15 00:00:17 +09:00
export type ChatModel = ModelType;
2024-02-20 19:04:32 +09:00
export interface MultimodalContent {
type: "text" | "image_url";
text?: string;
image_url?: {
url: string;
};
}
export interface RequestMessage {
2023-05-15 00:00:17 +09:00
role: MessageRole;
2024-02-20 19:04:32 +09:00
content: string | MultimodalContent[];
2023-05-15 00:00:17 +09:00
}
export interface LLMConfig {
model: string;
2024-07-05 20:59:45 +09:00
providerName?: string;
2023-05-15 00:00:17 +09:00
temperature?: number;
top_p?: number;
2023-05-15 00:00:17 +09:00
stream?: boolean;
presence_penalty?: number;
frequency_penalty?: number;
2024-08-02 19:50:48 +09:00
size?: DalleRequestPayload["size"];
quality?: DalleRequestPayload["quality"];
style?: DalleRequestPayload["style"];
2023-05-15 00:00:17 +09:00
}
2024-08-27 17:21:02 +09:00
export interface SpeechOptions {
model: string;
input: string;
voice: string;
response_format?: string;
speed?: number;
onController?: (controller: AbortController) => void;
}
2023-05-15 00:00:17 +09:00
export interface ChatOptions {
messages: RequestMessage[];
2023-05-15 00:00:17 +09:00
config: LLMConfig;
onUpdate?: (message: string, chunk: string) => void;
2023-05-15 00:00:17 +09:00
onFinish: (message: string) => void;
onError?: (err: Error) => void;
onController?: (controller: AbortController) => void;
2024-08-29 00:58:46 +09:00
onBeforeTool?: (tool: ChatMessageTool) => void;
onAfterTool?: (tool: ChatMessageTool) => void;
2023-05-15 00:00:17 +09:00
}
export interface LLMUsage {
used: number;
total: number;
}
export interface LLMModel {
name: string;
2024-07-05 20:59:45 +09:00
displayName?: string;
available: boolean;
2023-12-24 03:15:30 +09:00
provider: LLMModelProvider;
sorted: number;
2023-12-24 03:15:30 +09:00
}
export interface LLMModelProvider {
id: string;
providerName: string;
providerType: string;
sorted: number;
}
2023-05-15 00:00:17 +09:00
export abstract class LLMApi {
abstract chat(options: ChatOptions): Promise<void>;
2024-08-27 17:21:02 +09:00
abstract speech(options: SpeechOptions): Promise<ArrayBuffer>;
2023-05-15 00:00:17 +09:00
abstract usage(): Promise<LLMUsage>;
abstract models(): Promise<LLMModel[]>;
2023-05-15 00:00:17 +09:00
}
type ProviderName = "openai" | "azure" | "claude" | "palm";
interface Model {
name: string;
provider: ProviderName;
ctxlen: number;
}
interface ChatProvider {
name: ProviderName;
apiConfig: {
baseUrl: string;
apiKey: string;
summaryModel: Model;
};
models: Model[];
chat: () => void;
usage: () => void;
}
2023-05-15 00:00:17 +09:00
export class ClientApi {
public llm: LLMApi;
2023-12-24 03:15:30 +09:00
constructor(provider: ModelProvider = ModelProvider.GPT) {
2024-04-07 12:32:57 +09:00
switch (provider) {
case ModelProvider.GeminiPro:
this.llm = new GeminiProApi();
break;
case ModelProvider.Claude:
this.llm = new ClaudeApi();
break;
2024-07-06 14:05:09 +09:00
case ModelProvider.Ernie:
this.llm = new ErnieApi();
break;
2024-07-06 15:59:37 +09:00
case ModelProvider.Doubao:
this.llm = new DoubaoApi();
break;
2024-07-07 22:59:56 +09:00
case ModelProvider.Qwen:
this.llm = new QwenApi();
2024-08-05 13:45:25 +09:00
break;
2024-07-07 15:43:28 +09:00
case ModelProvider.Hunyuan:
this.llm = new HunyuanApi();
2024-07-07 22:59:56 +09:00
break;
2024-08-01 12:55:22 +09:00
case ModelProvider.Moonshot:
this.llm = new MoonshotApi();
break;
case ModelProvider.Iflytek:
this.llm = new SparkApi();
break;
2024-04-07 12:32:57 +09:00
default:
this.llm = new ChatGPTApi();
2023-12-24 03:15:30 +09:00
}
2023-05-15 00:00:17 +09:00
}
config() {}
prompts() {}
masks() {}
2023-05-25 02:04:37 +09:00
async share(messages: ChatMessage[], avatarUrl: string | null = null) {
const msgs = messages
.map((m) => ({
from: m.role === "user" ? "human" : "gpt",
value: m.content,
}))
.concat([
{
from: "human",
value:
2023-12-23 17:48:16 +09:00
"Share from [NextChat]: https://github.com/Yidadaa/ChatGPT-Next-Web",
2023-05-25 02:04:37 +09:00
},
]);
// 敬告二开开发者们,为了开源大模型的发展,请不要修改上述消息,此消息用于后续数据清洗使用
// Please do not modify this message
2023-07-13 00:19:46 +09:00
console.log("[Share]", messages, msgs);
2023-06-16 00:55:18 +09:00
const clientConfig = getClientConfig();
const proxyUrl = "/sharegpt";
const rawUrl = "https://sharegpt.com/api/conversations";
const shareUrl = clientConfig?.isApp ? rawUrl : proxyUrl;
const res = await fetch(shareUrl, {
2023-05-25 02:04:37 +09:00
body: JSON.stringify({
avatarUrl,
items: msgs,
}),
headers: {
"Content-Type": "application/json",
},
method: "POST",
});
const resJson = await res.json();
console.log("[Share]", resJson);
if (resJson.id) {
return `https://shareg.pt/${resJson.id}`;
}
}
2023-05-15 00:00:17 +09:00
}
2024-07-25 13:31:30 +09:00
export function getBearerToken(
apiKey: string,
noBearer: boolean = false,
): string {
return validString(apiKey)
? `${noBearer ? "" : "Bearer "}${apiKey.trim()}`
: "";
}
export function validString(x: string): boolean {
return x?.length > 0;
}
2024-09-18 15:57:43 +09:00
export function getHeaders(ignoreHeaders: boolean = false) {
2023-05-15 00:00:17 +09:00
const accessStore = useAccessStore.getState();
2024-07-06 12:16:00 +09:00
const chatStore = useChatStore.getState();
2024-08-28 14:13:41 +09:00
let headers: Record<string, string> = {};
if (!ignoreHeaders) {
headers = {
"Content-Type": "application/json",
Accept: "application/json",
};
}
2024-07-06 12:15:06 +09:00
const clientConfig = getClientConfig();
2023-05-15 00:00:17 +09:00
2024-07-06 12:15:06 +09:00
function getConfig() {
2024-07-06 12:16:00 +09:00
const modelConfig = chatStore.currentSession().mask.modelConfig;
2024-07-06 12:15:06 +09:00
const isGoogle = modelConfig.providerName == ServiceProvider.Google;
const isAzure = modelConfig.providerName === ServiceProvider.Azure;
const isAnthropic = modelConfig.providerName === ServiceProvider.Anthropic;
2024-07-09 20:21:27 +09:00
const isBaidu = modelConfig.providerName == ServiceProvider.Baidu;
const isByteDance = modelConfig.providerName === ServiceProvider.ByteDance;
2024-07-09 20:56:52 +09:00
const isAlibaba = modelConfig.providerName === ServiceProvider.Alibaba;
2024-08-01 12:55:22 +09:00
const isMoonshot = modelConfig.providerName === ServiceProvider.Moonshot;
const isIflytek = modelConfig.providerName === ServiceProvider.Iflytek;
2024-07-06 12:15:06 +09:00
const isEnabledAccessControl = accessStore.enabledAccessControl();
const apiKey = isGoogle
? accessStore.googleApiKey
: isAzure
? accessStore.azureApiKey
: isAnthropic
? accessStore.anthropicApiKey
2024-07-09 20:21:27 +09:00
: isByteDance
? accessStore.bytedanceApiKey
2024-07-09 20:56:52 +09:00
: isAlibaba
? accessStore.alibabaApiKey
2024-08-01 12:55:22 +09:00
: isMoonshot
? accessStore.moonshotApiKey
: isIflytek
? accessStore.iflytekApiKey && accessStore.iflytekApiSecret
? accessStore.iflytekApiKey + ":" + accessStore.iflytekApiSecret
: ""
2024-07-06 12:15:06 +09:00
: accessStore.openaiApiKey;
2024-07-09 20:21:27 +09:00
return {
isGoogle,
isAzure,
isAnthropic,
isBaidu,
isByteDance,
2024-07-09 20:56:52 +09:00
isAlibaba,
2024-08-01 12:55:22 +09:00
isMoonshot,
isIflytek,
2024-07-09 20:21:27 +09:00
apiKey,
isEnabledAccessControl,
};
2024-07-06 12:15:06 +09:00
}
function getAuthHeader(): string {
return isAzure
? "api-key"
: isAnthropic
? "x-api-key"
: isGoogle
? "x-goog-api-key"
: "Authorization";
2024-07-06 12:15:06 +09:00
}
2024-07-09 20:21:27 +09:00
const {
isGoogle,
isAzure,
isAnthropic,
isBaidu,
apiKey,
isEnabledAccessControl,
} = getConfig();
// when using baidu api in app, not set auth header
if (isBaidu && clientConfig?.isApp) return headers;
2024-07-06 12:15:06 +09:00
const authHeader = getAuthHeader();
const bearerToken = getBearerToken(
apiKey,
isAzure || isAnthropic || isGoogle,
);
2024-07-06 12:15:06 +09:00
if (bearerToken) {
headers[authHeader] = bearerToken;
} else if (isEnabledAccessControl && validString(accessStore.accessCode)) {
headers["Authorization"] = getBearerToken(
ACCESS_CODE_PREFIX + accessStore.accessCode,
);
2023-05-15 00:00:17 +09:00
}
return headers;
}
2024-07-06 12:27:53 +09:00
export function getClientApi(provider: ServiceProvider): ClientApi {
switch (provider) {
case ServiceProvider.Google:
return new ClientApi(ModelProvider.GeminiPro);
case ServiceProvider.Anthropic:
return new ClientApi(ModelProvider.Claude);
2024-07-06 22:25:00 +09:00
case ServiceProvider.Baidu:
return new ClientApi(ModelProvider.Ernie);
case ServiceProvider.ByteDance:
return new ClientApi(ModelProvider.Doubao);
2024-07-07 22:59:56 +09:00
case ServiceProvider.Alibaba:
return new ClientApi(ModelProvider.Qwen);
2024-07-07 15:43:28 +09:00
case ServiceProvider.Tencent:
return new ClientApi(ModelProvider.Hunyuan);
2024-08-01 12:55:22 +09:00
case ServiceProvider.Moonshot:
return new ClientApi(ModelProvider.Moonshot);
case ServiceProvider.Iflytek:
return new ClientApi(ModelProvider.Iflytek);
2024-07-06 12:27:53 +09:00
default:
return new ClientApi(ModelProvider.GPT);
}
}