plugin.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  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. 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. baseURL,
  61. headers,
  62. },
  63. });
  64. try {
  65. api.initSync();
  66. } catch (e) {}
  67. const operations = api.getOperations();
  68. return (this.tools[plugin.id] = {
  69. api,
  70. length: operations.length,
  71. tools: operations.map((o) => {
  72. // @ts-ignore
  73. const parameters = o?.requestBody?.content["application/json"]
  74. ?.schema || {
  75. type: "object",
  76. properties: {},
  77. };
  78. if (!parameters["required"]) {
  79. parameters["required"] = [];
  80. }
  81. if (o.parameters instanceof Array) {
  82. o.parameters.forEach((p) => {
  83. // @ts-ignore
  84. if (p?.in == "query" || p?.in == "path") {
  85. // const name = `${p.in}__${p.name}`
  86. // @ts-ignore
  87. const name = p?.name;
  88. parameters["properties"][name] = {
  89. // @ts-ignore
  90. type: p.schema.type,
  91. // @ts-ignore
  92. description: p.description,
  93. };
  94. // @ts-ignore
  95. if (p.required) {
  96. parameters["required"].push(name);
  97. }
  98. }
  99. });
  100. }
  101. return {
  102. type: "function",
  103. function: {
  104. name: o.operationId,
  105. description: o.description || o.summary,
  106. parameters: parameters,
  107. },
  108. } as FunctionToolItem;
  109. }),
  110. funcs: operations.reduce((s, o) => {
  111. // @ts-ignore
  112. s[o.operationId] = function (args) {
  113. const parameters: Record<string, any> = {};
  114. if (o.parameters instanceof Array) {
  115. o.parameters.forEach((p) => {
  116. // @ts-ignore
  117. parameters[p?.name] = args[p?.name];
  118. // @ts-ignore
  119. delete args[p?.name];
  120. });
  121. }
  122. if (authLocation == "query") {
  123. parameters[headerName] = tokenValue;
  124. } else if (authLocation == "body") {
  125. args[headerName] = tokenValue;
  126. }
  127. // @ts-ignore
  128. return api.client[o.operationId](
  129. parameters,
  130. args,
  131. api.axiosConfigDefaults,
  132. );
  133. };
  134. return s;
  135. }, {}),
  136. });
  137. },
  138. get(id: string) {
  139. return this.tools[id];
  140. },
  141. };
  142. export const createEmptyPlugin = () =>
  143. ({
  144. id: nanoid(),
  145. title: "",
  146. version: "1.0.0",
  147. content: "",
  148. builtin: false,
  149. createdAt: Date.now(),
  150. }) as Plugin;
  151. export const DEFAULT_PLUGIN_STATE = {
  152. plugins: {} as Record<string, Plugin>,
  153. };
  154. export const usePluginStore = createPersistStore(
  155. { ...DEFAULT_PLUGIN_STATE },
  156. (set, get) => ({
  157. create(plugin?: Partial<Plugin>) {
  158. const plugins = get().plugins;
  159. const id = nanoid();
  160. plugins[id] = {
  161. ...createEmptyPlugin(),
  162. ...plugin,
  163. id,
  164. builtin: false,
  165. };
  166. set(() => ({ plugins }));
  167. get().markUpdate();
  168. return plugins[id];
  169. },
  170. updatePlugin(id: string, updater: (plugin: Plugin) => void) {
  171. const plugins = get().plugins;
  172. const plugin = plugins[id];
  173. if (!plugin) return;
  174. const updatePlugin = { ...plugin };
  175. updater(updatePlugin);
  176. plugins[id] = updatePlugin;
  177. FunctionToolService.add(updatePlugin, true);
  178. set(() => ({ plugins }));
  179. get().markUpdate();
  180. },
  181. delete(id: string) {
  182. const plugins = get().plugins;
  183. delete plugins[id];
  184. set(() => ({ plugins }));
  185. get().markUpdate();
  186. },
  187. getAsTools(ids: string[]) {
  188. const plugins = get().plugins;
  189. const selected = ids
  190. .map((id) => plugins[id])
  191. .filter((i) => i)
  192. .map((p) => FunctionToolService.add(p));
  193. return [
  194. // @ts-ignore
  195. selected.reduce((s, i) => s.concat(i.tools), []),
  196. selected.reduce((s, i) => Object.assign(s, i.funcs), {}),
  197. ];
  198. },
  199. get(id?: string) {
  200. return get().plugins[id ?? 1145141919810];
  201. },
  202. getAll() {
  203. return Object.values(get().plugins).sort(
  204. (a, b) => b.createdAt - a.createdAt,
  205. );
  206. },
  207. }),
  208. {
  209. name: StoreKey.Plugin,
  210. version: 1,
  211. },
  212. );