mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-11-17 06:23:44 +08:00
Merge remote-tracking branch 'up/website' into website
# Conflicts: # app/components/chat.tsx # app/utils.ts
This commit is contained in:
@@ -183,7 +183,7 @@ export const useAccessStore = createPersistStore(
|
||||
this.isValidBaidu() ||
|
||||
this.isValidByteDance() ||
|
||||
this.isValidAlibaba() ||
|
||||
this.isValidTencent ||
|
||||
this.isValidTencent() ||
|
||||
this.isValidMoonshot() ||
|
||||
this.isValidIflytek() ||
|
||||
!this.enabledAccessControl() ||
|
||||
|
||||
@@ -32,6 +32,19 @@ import { collectModelsWithDefaultModel } from "../utils/model";
|
||||
import { useAccessStore } from "./access";
|
||||
import { useSyncStore } from "./sync";
|
||||
import { isDalle3 } from "../utils";
|
||||
import { indexedDBStorage } from "@/app/utils/indexedDB-storage";
|
||||
|
||||
export type ChatMessageTool = {
|
||||
id: string;
|
||||
index?: number;
|
||||
type?: string;
|
||||
function?: {
|
||||
name: string;
|
||||
arguments?: string;
|
||||
};
|
||||
content?: string;
|
||||
isError?: boolean;
|
||||
};
|
||||
|
||||
export type ChatMessage = RequestMessage & {
|
||||
date: string;
|
||||
@@ -39,6 +52,7 @@ export type ChatMessage = RequestMessage & {
|
||||
isError?: boolean;
|
||||
id: string;
|
||||
model?: ModelType;
|
||||
tools?: ChatMessageTool[];
|
||||
};
|
||||
|
||||
export function createMessage(override: Partial<ChatMessage>): ChatMessage {
|
||||
@@ -464,8 +478,24 @@ export const useChatStore = createPersistStore(
|
||||
}
|
||||
ChatControllerPool.remove(session.id, botMessage.id);
|
||||
},
|
||||
onBeforeTool(tool: ChatMessageTool) {
|
||||
(botMessage.tools = botMessage?.tools || []).push(tool);
|
||||
get().updateCurrentSession((session) => {
|
||||
session.messages = session.messages.concat();
|
||||
});
|
||||
},
|
||||
onAfterTool(tool: ChatMessageTool) {
|
||||
botMessage?.tools?.forEach((t, i, tools) => {
|
||||
if (tool.id == t.id) {
|
||||
tools[i] = { ...tool };
|
||||
}
|
||||
});
|
||||
get().updateCurrentSession((session) => {
|
||||
session.messages = session.messages.concat();
|
||||
});
|
||||
},
|
||||
onError(error) {
|
||||
const isAborted = error.message.includes("aborted");
|
||||
const isAborted = error.message?.includes?.("aborted");
|
||||
botMessage.content +=
|
||||
"\n\n" +
|
||||
prettyObject({
|
||||
@@ -740,7 +770,8 @@ export const useChatStore = createPersistStore(
|
||||
set(() => ({ sessions }));
|
||||
},
|
||||
|
||||
clearAllData() {
|
||||
async clearAllData() {
|
||||
await indexedDBStorage.clear();
|
||||
localStorage.clear();
|
||||
location.reload();
|
||||
},
|
||||
|
||||
@@ -2,3 +2,4 @@ export * from "./chat";
|
||||
export * from "./update";
|
||||
export * from "./access";
|
||||
export * from "./config";
|
||||
export * from "./plugin";
|
||||
|
||||
@@ -2,7 +2,7 @@ import { BUILTIN_MASKS } from "../masks";
|
||||
import { getLang, Lang } from "../locales";
|
||||
import { DEFAULT_TOPIC, ChatMessage } from "./chat";
|
||||
import { ModelConfig, useAppConfig } from "./config";
|
||||
import { StoreKey, Plugin } from "../constant";
|
||||
import { StoreKey } from "../constant";
|
||||
import { nanoid } from "nanoid";
|
||||
import { createPersistStore } from "../utils/store";
|
||||
|
||||
@@ -17,7 +17,8 @@ export type Mask = {
|
||||
modelConfig: ModelConfig;
|
||||
lang: Lang;
|
||||
builtin: boolean;
|
||||
plugin?: Plugin[];
|
||||
plugin?: string[];
|
||||
enableArtifacts?: boolean;
|
||||
};
|
||||
|
||||
export const DEFAULT_MASK_STATE = {
|
||||
@@ -38,7 +39,7 @@ export const createEmptyMask = () =>
|
||||
lang: getLang(),
|
||||
builtin: false,
|
||||
createdAt: Date.now(),
|
||||
plugin: [Plugin.Artifacts],
|
||||
plugin: [],
|
||||
}) as Mask;
|
||||
|
||||
export const useMaskStore = createPersistStore(
|
||||
|
||||
225
app/store/plugin.ts
Normal file
225
app/store/plugin.ts
Normal file
@@ -0,0 +1,225 @@
|
||||
import OpenAPIClientAxios from "openapi-client-axios";
|
||||
import { getLang, Lang } from "../locales";
|
||||
import { StoreKey } from "../constant";
|
||||
import { nanoid } from "nanoid";
|
||||
import { createPersistStore } from "../utils/store";
|
||||
import yaml from "js-yaml";
|
||||
import { adapter } from "../utils";
|
||||
|
||||
export type Plugin = {
|
||||
id: string;
|
||||
createdAt: number;
|
||||
title: string;
|
||||
version: string;
|
||||
content: string;
|
||||
builtin: boolean;
|
||||
authType?: string;
|
||||
authLocation?: string;
|
||||
authHeader?: string;
|
||||
authToken?: string;
|
||||
usingProxy?: boolean;
|
||||
};
|
||||
|
||||
export type FunctionToolItem = {
|
||||
type: string;
|
||||
function: {
|
||||
name: string;
|
||||
description?: string;
|
||||
parameters: Object;
|
||||
};
|
||||
};
|
||||
|
||||
type FunctionToolServiceItem = {
|
||||
api: OpenAPIClientAxios;
|
||||
length: number;
|
||||
tools: FunctionToolItem[];
|
||||
funcs: Record<string, Function>;
|
||||
};
|
||||
|
||||
export const FunctionToolService = {
|
||||
tools: {} as Record<string, FunctionToolServiceItem>,
|
||||
add(plugin: Plugin, replace = false) {
|
||||
if (!replace && this.tools[plugin.id]) return this.tools[plugin.id];
|
||||
const headerName = (
|
||||
plugin?.authType == "custom" ? plugin?.authHeader : "Authorization"
|
||||
) as string;
|
||||
const tokenValue =
|
||||
plugin?.authType == "basic"
|
||||
? `Basic ${plugin?.authToken}`
|
||||
: plugin?.authType == "bearer"
|
||||
? ` Bearer ${plugin?.authToken}`
|
||||
: plugin?.authToken;
|
||||
const authLocation = plugin?.authLocation || "header";
|
||||
const definition = yaml.load(plugin.content) as any;
|
||||
const serverURL = definition?.servers?.[0]?.url;
|
||||
const baseURL = !!plugin?.usingProxy ? "/api/proxy" : serverURL;
|
||||
const headers: Record<string, string | undefined> = {
|
||||
"X-Base-URL": !!plugin?.usingProxy ? serverURL : undefined,
|
||||
};
|
||||
if (authLocation == "header") {
|
||||
headers[headerName] = tokenValue;
|
||||
}
|
||||
const api = new OpenAPIClientAxios({
|
||||
definition: yaml.load(plugin.content) as any,
|
||||
axiosConfigDefaults: {
|
||||
adapter: (window.__TAURI__ ? adapter : ["xhr"]) as any,
|
||||
baseURL,
|
||||
headers,
|
||||
},
|
||||
});
|
||||
try {
|
||||
api.initSync();
|
||||
} catch (e) {}
|
||||
const operations = api.getOperations();
|
||||
return (this.tools[plugin.id] = {
|
||||
api,
|
||||
length: operations.length,
|
||||
tools: operations.map((o) => {
|
||||
// @ts-ignore
|
||||
const parameters = o?.requestBody?.content["application/json"]
|
||||
?.schema || {
|
||||
type: "object",
|
||||
properties: {},
|
||||
};
|
||||
if (!parameters["required"]) {
|
||||
parameters["required"] = [];
|
||||
}
|
||||
if (o.parameters instanceof Array) {
|
||||
o.parameters.forEach((p) => {
|
||||
// @ts-ignore
|
||||
if (p?.in == "query" || p?.in == "path") {
|
||||
// const name = `${p.in}__${p.name}`
|
||||
// @ts-ignore
|
||||
const name = p?.name;
|
||||
parameters["properties"][name] = {
|
||||
// @ts-ignore
|
||||
type: p.schema.type,
|
||||
// @ts-ignore
|
||||
description: p.description,
|
||||
};
|
||||
// @ts-ignore
|
||||
if (p.required) {
|
||||
parameters["required"].push(name);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return {
|
||||
type: "function",
|
||||
function: {
|
||||
name: o.operationId,
|
||||
description: o.description || o.summary,
|
||||
parameters: parameters,
|
||||
},
|
||||
} as FunctionToolItem;
|
||||
}),
|
||||
funcs: operations.reduce((s, o) => {
|
||||
// @ts-ignore
|
||||
s[o.operationId] = function (args) {
|
||||
const parameters: Record<string, any> = {};
|
||||
if (o.parameters instanceof Array) {
|
||||
o.parameters.forEach((p) => {
|
||||
// @ts-ignore
|
||||
parameters[p?.name] = args[p?.name];
|
||||
// @ts-ignore
|
||||
delete args[p?.name];
|
||||
});
|
||||
}
|
||||
if (authLocation == "query") {
|
||||
parameters[headerName] = tokenValue;
|
||||
} else if (authLocation == "body") {
|
||||
args[headerName] = tokenValue;
|
||||
}
|
||||
// @ts-ignore
|
||||
return api.client[o.operationId](
|
||||
parameters,
|
||||
args,
|
||||
api.axiosConfigDefaults,
|
||||
);
|
||||
};
|
||||
return s;
|
||||
}, {}),
|
||||
});
|
||||
},
|
||||
get(id: string) {
|
||||
return this.tools[id];
|
||||
},
|
||||
};
|
||||
|
||||
export const createEmptyPlugin = () =>
|
||||
({
|
||||
id: nanoid(),
|
||||
title: "",
|
||||
version: "1.0.0",
|
||||
content: "",
|
||||
builtin: false,
|
||||
createdAt: Date.now(),
|
||||
}) as Plugin;
|
||||
|
||||
export const DEFAULT_PLUGIN_STATE = {
|
||||
plugins: {} as Record<string, Plugin>,
|
||||
};
|
||||
|
||||
export const usePluginStore = createPersistStore(
|
||||
{ ...DEFAULT_PLUGIN_STATE },
|
||||
|
||||
(set, get) => ({
|
||||
create(plugin?: Partial<Plugin>) {
|
||||
const plugins = get().plugins;
|
||||
const id = nanoid();
|
||||
plugins[id] = {
|
||||
...createEmptyPlugin(),
|
||||
...plugin,
|
||||
id,
|
||||
builtin: false,
|
||||
};
|
||||
|
||||
set(() => ({ plugins }));
|
||||
get().markUpdate();
|
||||
|
||||
return plugins[id];
|
||||
},
|
||||
updatePlugin(id: string, updater: (plugin: Plugin) => void) {
|
||||
const plugins = get().plugins;
|
||||
const plugin = plugins[id];
|
||||
if (!plugin) return;
|
||||
const updatePlugin = { ...plugin };
|
||||
updater(updatePlugin);
|
||||
plugins[id] = updatePlugin;
|
||||
FunctionToolService.add(updatePlugin, true);
|
||||
set(() => ({ plugins }));
|
||||
get().markUpdate();
|
||||
},
|
||||
delete(id: string) {
|
||||
const plugins = get().plugins;
|
||||
delete plugins[id];
|
||||
set(() => ({ plugins }));
|
||||
get().markUpdate();
|
||||
},
|
||||
|
||||
getAsTools(ids: string[]) {
|
||||
const plugins = get().plugins;
|
||||
const selected = ids
|
||||
.map((id) => plugins[id])
|
||||
.filter((i) => i)
|
||||
.map((p) => FunctionToolService.add(p));
|
||||
return [
|
||||
// @ts-ignore
|
||||
selected.reduce((s, i) => s.concat(i.tools), []),
|
||||
selected.reduce((s, i) => Object.assign(s, i.funcs), {}),
|
||||
];
|
||||
},
|
||||
get(id?: string) {
|
||||
return get().plugins[id ?? 1145141919810];
|
||||
},
|
||||
getAll() {
|
||||
return Object.values(get().plugins).sort(
|
||||
(a, b) => b.createdAt - a.createdAt,
|
||||
);
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: StoreKey.Plugin,
|
||||
version: 1,
|
||||
},
|
||||
);
|
||||
Reference in New Issue
Block a user