import OpenAPIClientAxios from "openapi-client-axios"; 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; }; export const FunctionToolService = { tools: {} as Record, 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 = { "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 = {}; 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, }; export const usePluginStore = createPersistStore( { ...DEFAULT_PLUGIN_STATE }, (set, get) => ({ create(plugin?: Partial) { 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, }, );