plugin.ts 6.0 KB

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