plugin.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. import OpenAPIClientAxios from "openapi-client-axios";
  2. import { getLang, Lang } from "../locales";
  3. import { StoreKey } from "../constant";
  4. import { nanoid } from "nanoid";
  5. import { createPersistStore } from "../utils/store";
  6. import yaml from "js-yaml";
  7. import { adapter } from "../utils";
  8. export type Plugin = {
  9. id: string;
  10. createdAt: number;
  11. title: string;
  12. version: string;
  13. content: string;
  14. builtin: boolean;
  15. authType?: string;
  16. authLocation?: string;
  17. authHeader?: string;
  18. authToken?: string;
  19. usingProxy?: boolean;
  20. };
  21. export type FunctionToolItem = {
  22. type: string;
  23. function: {
  24. name: string;
  25. description?: string;
  26. parameters: Object;
  27. };
  28. };
  29. type FunctionToolServiceItem = {
  30. api: OpenAPIClientAxios;
  31. length: number;
  32. tools: FunctionToolItem[];
  33. funcs: Record<string, Function>;
  34. };
  35. export const FunctionToolService = {
  36. tools: {} as Record<string, FunctionToolServiceItem>,
  37. add(plugin: Plugin, replace = false) {
  38. if (!replace && this.tools[plugin.id]) return this.tools[plugin.id];
  39. const headerName = (
  40. plugin?.authType == "custom" ? plugin?.authHeader : "Authorization"
  41. ) as string;
  42. const tokenValue =
  43. plugin?.authType == "basic"
  44. ? `Basic ${plugin?.authToken}`
  45. : plugin?.authType == "bearer"
  46. ? ` Bearer ${plugin?.authToken}`
  47. : plugin?.authToken;
  48. const authLocation = plugin?.authLocation || "header";
  49. const definition = yaml.load(plugin.content) as any;
  50. const serverURL = definition?.servers?.[0]?.url;
  51. const baseURL = !!plugin?.usingProxy ? "/api/proxy" : serverURL;
  52. const headers: Record<string, string | undefined> = {
  53. "X-Base-URL": !!plugin?.usingProxy ? serverURL : undefined,
  54. };
  55. if (authLocation == "header") {
  56. headers[headerName] = tokenValue;
  57. }
  58. const api = new OpenAPIClientAxios({
  59. definition: yaml.load(plugin.content) as any,
  60. axiosConfigDefaults: {
  61. adapter: (window.__TAURI__ ? adapter : ["xhr"]) as any,
  62. baseURL,
  63. headers,
  64. },
  65. });
  66. try {
  67. api.initSync();
  68. } catch (e) {}
  69. const operations = api.getOperations();
  70. return (this.tools[plugin.id] = {
  71. api,
  72. length: operations.length,
  73. tools: operations.map((o) => {
  74. // @ts-ignore
  75. const parameters = o?.requestBody?.content["application/json"]
  76. ?.schema || {
  77. type: "object",
  78. properties: {},
  79. };
  80. if (!parameters["required"]) {
  81. parameters["required"] = [];
  82. }
  83. if (o.parameters instanceof Array) {
  84. o.parameters.forEach((p) => {
  85. // @ts-ignore
  86. if (p?.in == "query" || p?.in == "path") {
  87. // const name = `${p.in}__${p.name}`
  88. // @ts-ignore
  89. const name = p?.name;
  90. parameters["properties"][name] = {
  91. // @ts-ignore
  92. type: p.schema.type,
  93. // @ts-ignore
  94. description: p.description,
  95. };
  96. // @ts-ignore
  97. if (p.required) {
  98. parameters["required"].push(name);
  99. }
  100. }
  101. });
  102. }
  103. return {
  104. type: "function",
  105. function: {
  106. name: o.operationId,
  107. description: o.description || o.summary,
  108. parameters: parameters,
  109. },
  110. } as FunctionToolItem;
  111. }),
  112. funcs: operations.reduce((s, o) => {
  113. // @ts-ignore
  114. s[o.operationId] = function (args) {
  115. const parameters: Record<string, any> = {};
  116. if (o.parameters instanceof Array) {
  117. o.parameters.forEach((p) => {
  118. // @ts-ignore
  119. parameters[p?.name] = args[p?.name];
  120. // @ts-ignore
  121. delete args[p?.name];
  122. });
  123. }
  124. if (authLocation == "query") {
  125. parameters[headerName] = tokenValue;
  126. } else if (authLocation == "body") {
  127. args[headerName] = tokenValue;
  128. }
  129. // @ts-ignore
  130. return api.client[o.operationId](
  131. parameters,
  132. args,
  133. api.axiosConfigDefaults,
  134. );
  135. };
  136. return s;
  137. }, {}),
  138. });
  139. },
  140. get(id: string) {
  141. return this.tools[id];
  142. },
  143. };
  144. export const createEmptyPlugin = () =>
  145. ({
  146. id: nanoid(),
  147. title: "",
  148. version: "1.0.0",
  149. content: "",
  150. builtin: false,
  151. createdAt: Date.now(),
  152. }) as Plugin;
  153. export const DEFAULT_PLUGIN_STATE = {
  154. plugins: {} as Record<string, Plugin>,
  155. };
  156. export const usePluginStore = createPersistStore(
  157. { ...DEFAULT_PLUGIN_STATE },
  158. (set, get) => ({
  159. create(plugin?: Partial<Plugin>) {
  160. const plugins = get().plugins;
  161. const id = nanoid();
  162. plugins[id] = {
  163. ...createEmptyPlugin(),
  164. ...plugin,
  165. id,
  166. builtin: false,
  167. };
  168. set(() => ({ plugins }));
  169. get().markUpdate();
  170. return plugins[id];
  171. },
  172. updatePlugin(id: string, updater: (plugin: Plugin) => void) {
  173. const plugins = get().plugins;
  174. const plugin = plugins[id];
  175. if (!plugin) return;
  176. const updatePlugin = { ...plugin };
  177. updater(updatePlugin);
  178. plugins[id] = updatePlugin;
  179. FunctionToolService.add(updatePlugin, true);
  180. set(() => ({ plugins }));
  181. get().markUpdate();
  182. },
  183. delete(id: string) {
  184. const plugins = get().plugins;
  185. delete plugins[id];
  186. set(() => ({ plugins }));
  187. get().markUpdate();
  188. },
  189. getAsTools(ids: string[]) {
  190. const plugins = get().plugins;
  191. const selected = (ids || [])
  192. .map((id) => plugins[id])
  193. .filter((i) => i)
  194. .map((p) => FunctionToolService.add(p));
  195. return [
  196. // @ts-ignore
  197. selected.reduce((s, i) => s.concat(i.tools), []),
  198. selected.reduce((s, i) => Object.assign(s, i.funcs), {}),
  199. ];
  200. },
  201. get(id?: string) {
  202. return get().plugins[id ?? 1145141919810];
  203. },
  204. getAll() {
  205. return Object.values(get().plugins).sort(
  206. (a, b) => b.createdAt - a.createdAt,
  207. );
  208. },
  209. }),
  210. {
  211. name: StoreKey.Plugin,
  212. version: 1,
  213. },
  214. );