import type { ChatMessage, ChatMessageTool, ModelType, } from '../store'; import type { DalleRequestPayload } from './platforms/openai'; import { getClientConfig } from '../config/client'; import { ACCESS_CODE_PREFIX, ModelProvider, ServiceProvider, } from '../constant'; import { useAccessStore, useChatStore, } from '../store'; import { QwenApi } from './platforms/alibaba'; import { ClaudeApi } from './platforms/anthropic'; import { ErnieApi } from './platforms/baidu'; import { DoubaoApi } from './platforms/bytedance'; import { ChatGLMApi } from './platforms/glm'; import { GeminiProApi } from './platforms/google'; import { SparkApi } from './platforms/iflytek'; import { MoonshotApi } from './platforms/moonshot'; import { ChatGPTApi } from './platforms/openai'; import { HunyuanApi } from './platforms/tencent'; import { XAIApi } from './platforms/xai'; export const ROLES = ['system', 'user', 'assistant'] as const; export type MessageRole = (typeof ROLES)[number]; export const Models = ['gpt-3.5-turbo', 'gpt-4'] as const; export const TTSModels = ['tts-1', 'tts-1-hd'] as const; export type ChatModel = ModelType; export interface MultimodalContent { type: 'text' | 'image_url'; text?: string; image_url?: { url: string; }; } export interface RequestMessage { role: MessageRole; content: string | MultimodalContent[]; } export interface LLMConfig { model: string; providerName?: string; temperature?: number; top_p?: number; stream?: boolean; presence_penalty?: number; frequency_penalty?: number; size?: DalleRequestPayload['size']; quality?: DalleRequestPayload['quality']; style?: DalleRequestPayload['style']; } export interface SpeechOptions { model: string; input: string; voice: string; response_format?: string; speed?: number; onController?: (controller: AbortController) => void; } export interface ChatOptions { messages: RequestMessage[]; config: LLMConfig; onUpdate?: (message: string, chunk: string) => void; onFinish: (message: string, responseRes: Response) => void; onError?: (err: Error) => void; onController?: (controller: AbortController) => void; onBeforeTool?: (tool: ChatMessageTool) => void; onAfterTool?: (tool: ChatMessageTool) => void; } export interface LLMUsage { used: number; total: number; } export interface LLMModel { name: string; displayName?: string; available: boolean; provider: LLMModelProvider; sorted: number; } export interface LLMModelProvider { id: string; providerName: string; providerType: string; sorted: number; } export abstract class LLMApi { abstract chat(options: ChatOptions): Promise; abstract speech(options: SpeechOptions): Promise; abstract usage(): Promise; abstract models(): Promise; } 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; } export class ClientApi { public llm: LLMApi; constructor(provider: ModelProvider = ModelProvider.GPT) { switch (provider) { case ModelProvider.GeminiPro: this.llm = new GeminiProApi(); break; case ModelProvider.Claude: this.llm = new ClaudeApi(); break; case ModelProvider.Ernie: this.llm = new ErnieApi(); break; case ModelProvider.Doubao: this.llm = new DoubaoApi(); break; case ModelProvider.Qwen: this.llm = new QwenApi(); break; case ModelProvider.Hunyuan: this.llm = new HunyuanApi(); break; case ModelProvider.Moonshot: this.llm = new MoonshotApi(); break; case ModelProvider.Iflytek: this.llm = new SparkApi(); break; case ModelProvider.XAI: this.llm = new XAIApi(); break; case ModelProvider.ChatGLM: this.llm = new ChatGLMApi(); break; default: this.llm = new ChatGPTApi(); } } config() {} prompts() {} masks() {} 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: 'Share from [NextChat]: https://github.com/Yidadaa/ChatGPT-Next-Web', }, ]); // 敬告二开开发者们,为了开源大模型的发展,请不要修改上述消息,此消息用于后续数据清洗使用 // Please do not modify this message console.log('[Share]', messages, msgs); const clientConfig = getClientConfig(); const proxyUrl = '/sharegpt'; const rawUrl = 'https://sharegpt.com/api/conversations'; const shareUrl = clientConfig?.isApp ? rawUrl : proxyUrl; const res = await fetch(shareUrl, { 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}`; } } } 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; } export function getHeaders(ignoreHeaders: boolean = false) { const accessStore = useAccessStore.getState(); const chatStore = useChatStore.getState(); let headers: Record = {}; if (!ignoreHeaders) { headers = { 'Content-Type': 'application/json', 'Accept': 'application/json', }; } const clientConfig = getClientConfig(); function getConfig() { const modelConfig = chatStore.currentSession().mask.modelConfig; const isGoogle = modelConfig.providerName === ServiceProvider.Google; const isAzure = modelConfig.providerName === ServiceProvider.Azure; const isAnthropic = modelConfig.providerName === ServiceProvider.Anthropic; const isBaidu = modelConfig.providerName == ServiceProvider.Baidu; const isByteDance = modelConfig.providerName === ServiceProvider.ByteDance; const isAlibaba = modelConfig.providerName === ServiceProvider.Alibaba; const isMoonshot = modelConfig.providerName === ServiceProvider.Moonshot; const isIflytek = modelConfig.providerName === ServiceProvider.Iflytek; const isXAI = modelConfig.providerName === ServiceProvider.XAI; const isChatGLM = modelConfig.providerName === ServiceProvider.ChatGLM; const isEnabledAccessControl = accessStore.enabledAccessControl(); const apiKey = isGoogle ? accessStore.googleApiKey : isAzure ? accessStore.azureApiKey : isAnthropic ? accessStore.anthropicApiKey : isByteDance ? accessStore.bytedanceApiKey : isAlibaba ? accessStore.alibabaApiKey : isMoonshot ? accessStore.moonshotApiKey : isXAI ? accessStore.xaiApiKey : isChatGLM ? accessStore.chatglmApiKey : isIflytek ? accessStore.iflytekApiKey && accessStore.iflytekApiSecret ? `${accessStore.iflytekApiKey}:${accessStore.iflytekApiSecret}` : '' : accessStore.openaiApiKey; return { isGoogle, isAzure, isAnthropic, isBaidu, isByteDance, isAlibaba, isMoonshot, isIflytek, isXAI, isChatGLM, apiKey, isEnabledAccessControl, }; } function getAuthHeader(): string { return isAzure ? 'api-key' : isAnthropic ? 'x-api-key' : isGoogle ? 'x-goog-api-key' : 'Authorization'; } const { isGoogle, isAzure, isAnthropic, isBaidu, apiKey, isEnabledAccessControl, } = getConfig(); // when using baidu api in app, not set auth header if (isBaidu && clientConfig?.isApp) { return headers; } 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, ); } return headers; } export function getClientApi(provider: ServiceProvider): ClientApi { switch (provider) { case ServiceProvider.Google: return new ClientApi(ModelProvider.GeminiPro); case ServiceProvider.Anthropic: return new ClientApi(ModelProvider.Claude); case ServiceProvider.Baidu: return new ClientApi(ModelProvider.Ernie); case ServiceProvider.ByteDance: return new ClientApi(ModelProvider.Doubao); case ServiceProvider.Alibaba: return new ClientApi(ModelProvider.Qwen); case ServiceProvider.Tencent: return new ClientApi(ModelProvider.Hunyuan); case ServiceProvider.Moonshot: return new ClientApi(ModelProvider.Moonshot); case ServiceProvider.Iflytek: return new ClientApi(ModelProvider.Iflytek); case ServiceProvider.XAI: return new ClientApi(ModelProvider.XAI); case ServiceProvider.ChatGLM: return new ClientApi(ModelProvider.ChatGLM); default: return new ClientApi(ModelProvider.GPT); } }