|
|
import { getClientConfig } from "../config/client"; |
|
|
import { ACCESS_CODE_PREFIX } from "../constant"; |
|
|
import { ChatMessage, ModelType, useAccessStore } from "../store"; |
|
|
import { ChatGPTApi } from "./platforms/openai"; |
|
|
|
|
|
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 type ChatModel = ModelType; |
|
|
|
|
|
export interface RequestMessage { |
|
|
role: MessageRole; |
|
|
content: string; |
|
|
} |
|
|
|
|
|
export interface LLMConfig { |
|
|
model: string; |
|
|
temperature?: number; |
|
|
top_p?: number; |
|
|
stream?: boolean; |
|
|
presence_penalty?: number; |
|
|
frequency_penalty?: number; |
|
|
} |
|
|
|
|
|
export interface ChatOptions { |
|
|
messages: RequestMessage[]; |
|
|
config: LLMConfig; |
|
|
|
|
|
onUpdate?: (message: string, chunk: string) => void; |
|
|
onFinish: (message: string) => void; |
|
|
onError?: (err: Error) => void; |
|
|
onController?: (controller: AbortController) => void; |
|
|
} |
|
|
|
|
|
export interface LLMUsage { |
|
|
used: number; |
|
|
total: number; |
|
|
} |
|
|
|
|
|
export interface LLMModel { |
|
|
name: string; |
|
|
available: boolean; |
|
|
} |
|
|
|
|
|
export abstract class LLMApi { |
|
|
abstract chat(options: ChatOptions): Promise<void>; |
|
|
abstract usage(): Promise<LLMUsage>; |
|
|
abstract models(): Promise<LLMModel[]>; |
|
|
} |
|
|
|
|
|
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() { |
|
|
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 [ChatGPT Next Web]: https://github.com/Yidadaa/ChatGPT-Next-Web", |
|
|
}, |
|
|
]); |
|
|
|
|
|
|
|
|
|
|
|
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 const api = new ClientApi(); |
|
|
|
|
|
export function getHeaders() { |
|
|
const accessStore = useAccessStore.getState(); |
|
|
let headers: Record<string, string> = { |
|
|
"Content-Type": "application/json", |
|
|
"x-requested-with": "XMLHttpRequest", |
|
|
}; |
|
|
|
|
|
const makeBearer = (token: string) => `Bearer ${token.trim()}`; |
|
|
const validString = (x: string) => x && x.length > 0; |
|
|
|
|
|
|
|
|
if (validString(accessStore.token)) { |
|
|
headers.Authorization = makeBearer(accessStore.token); |
|
|
} else if ( |
|
|
accessStore.enabledAccessControl() && |
|
|
validString(accessStore.accessCode) |
|
|
) { |
|
|
headers.Authorization = makeBearer( |
|
|
ACCESS_CODE_PREFIX + accessStore.accessCode, |
|
|
); |
|
|
} |
|
|
|
|
|
if (validString(accessStore.midjourneyProxyUrl)) { |
|
|
headers["midjourney-proxy-url"] = accessStore.midjourneyProxyUrl; |
|
|
} |
|
|
|
|
|
return headers; |
|
|
} |
|
|
|
|
|
export function useGetMidjourneySelfProxyUrl(url: string) { |
|
|
const accessStore = useAccessStore.getState(); |
|
|
if (accessStore.useMjImgSelfProxy) { |
|
|
url = url.replace("https://cdn.discordapp.com", "/api/cnd-discordapp"); |
|
|
if (accessStore.accessCode) { |
|
|
url += |
|
|
(url.includes("?") ? "&" : "?") + |
|
|
"Authorization=" + |
|
|
accessStore.accessCode; |
|
|
} |
|
|
} |
|
|
return url; |
|
|
} |
|
|
|